簡體   English   中英

在Spring Boot中無法攔截和操作HttpServletResponse

[英]Unable to intercept and manipulate HttpServletResponse in Spring Boot

我要求Base64解碼我的Spring Boot服務接收的每個JSON請求有效負載。 在使用HTTP POST方法進行發布之前,JSON有效負載將在客戶端進行Base64編碼。 此外,在呈現給調用客戶端應用程序之前,我還需要Base64編碼JSON響應。

我需要通過使用處理程序攔截器來減少樣板代碼。 我已經通過使用攔截器實現了操作的請求/傳入分支,但尚未針對響應分支實現。 我已經在下面發布了代碼片段。 攔截響應並對其進行base64編碼的代碼在攔截器類的postHandle方法中。

我在這里做錯了什么?

攔截器類別:

public class Base64ResponseEncodingInterceptor implements HandlerInterceptor {   
    private static final String DECODED_REQUEST_ATTRIB = "decodedRequest";
    private static final String POST = "POST";


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView arg3) throws Exception {
          try {
              if (POST.equalsIgnoreCase(request.getMethod())) {
                    CharResponseWrapper res = new CharResponseWrapper(response);
                    res.getWriter();
                    byte[] encoded = Base64.encodeBase64(res.toString().getBytes());
                    byte[] encoded = Base64.encodeBase64(response.getHeader(ENCODED_RESPONSE_ATTRIB).getBytes());
                    response.getWriter().write(new String(encoded));
              }
          } catch (Exception e) {
              throw new Exception(e.getMessage());
          }
    }


    // preHandle and afterCompletion methods
    // Omitted 
}

上面使用的CharResponseWrapper類:

public class CharResponseWrapper extends HttpServletResponseWrapper {

    protected CharArrayWriter charWriter;

    protected PrintWriter writer;

    protected boolean getOutputStreamCalled;

    protected boolean getWriterCalled;


    public CharResponseWrapper(HttpServletResponse response) {
        super(response);
        charWriter = new CharArrayWriter();
    }


    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        if (getWriterCalled) {
            throw new IllegalStateException("getWriter already called");
        }
        getOutputStreamCalled = true;
        return super.getOutputStream();
    }


    @Override
    public PrintWriter getWriter() throws IOException {
        if (writer != null) {
            return writer;
        }
        if (getOutputStreamCalled) {
            throw new IllegalStateException("getOutputStream already called");
        }
        getWriterCalled = true;
        writer = new PrintWriter(charWriter);
        return writer;
    }


    @Override
    public String toString() {
        String s = null;
        if (writer != null) {
            s = charWriter.toString();
        }
        return s;
    }
}

注冊了Interceptor的JavaConfig類:

@Configuration
@EnableJpaRepositories(repositoryBaseClass = BaseRepositoryBean.class, basePackages = "")
@EntityScan(basePackages = { "com.companyname", "com.companyname.productname"})
public class RestConfig extends WebMvcConfigurerAdapter {


      @Override
      public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new Base64ResponseEncodingInterceptor());
      }

}

使用攔截器的控制器類(此處僅顯示工作請求分支):

@Autowired
HttpServletRequest request;

String decodedRequest = null;

@ModelAttribute("decodedRequest")
public void getDecodedParam(){
    decodedRequest = (String) request.getAttribute("decodedRequest");
} 

postHandle方法中的代碼不起作用。 它是HttpServletResponsenull還是收到異常消息:

getOutputStream已經被調用

更新:解決方法可直接在ResponseBodyAdvice中讀取響應 。在Controller類中,我添加了以下內容:

@RestController
@RequestMapping("/api/ipmanager")
public class IPProfileRestController extends AbstractRestController {

    @Autowired
    HttpServletResponse response;

   String encodedResponse = null;

   @ModelAttribute("encodedResponse")
    public void getEncodedResponse(){
       response.setHeader("encodedResponse", StringUtils.EMPTY);
    } 

    @RequestMapping(value = "/time", method = { RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE }, consumes = {
            MediaType.APPLICATION_JSON_VALUE })
    public @ResponseBody String saveAccessClientTime(@RequestBody String ecodedRequest) {

        // Some code here

        String controllerResponse = prettyJson(iPProfileResponse);
        response.setHeader("encodedResponse", controllerResponse);
        return controllerResponse;
    }
}

我在ResponseBodyAdvice中具有以下內容

@ControllerAdvice
public class Base64EncodedResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, 
                            Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body,
                                  MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> converterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {

        String body1 = StringUtils.EMPTY;
        // Encode the response and return

        List<String> listOfHeaderValues = response.getHeaders().get("encodedResponse");

        body1=new String(Base64.encodeBase64(listOfHeaderValues.get(0).getBytes()));

        return body1;
    }

}

Spring MVC文檔所述:

HandlerInterceptorpostHandle方法並不總是非常適合與@ResponseBodyResponseEntity方法一起使用。 在這種情況下, HttpMessageConverterpostHandle之前寫入並提交響應,這使得無法更改響應 ,例如添加標頭。 相反,應用程序可以實現ResponseBodyAdvice並將其聲明為@ControllerAdvice bean或直接在RequestMappingHandlerAdapter上對其進行配置。

話雖這么說:

我在這里做錯了什么?

由於響應已經提交,因此無法更改。 為了更改響應,您應該注冊ResponseBodyAdvice<T>並將響應編碼邏輯放在此處:

@ControllerAdvice
public class Base64EncodedResponseBodyAdvice implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(MethodParameter returnType, 
                            Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body,
                                  MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> converterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {

        // Encode the response and return
    }
}

如果您的方法返回ResponseEntity<T>則將此代碼寫入HandlerInterceptor postHandle()方法中:

例如。 response.addHeader("jwt_token", "kajdlakjd");

它會工作!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM