无法在Spring Boot中拦截和操作HttpServletResponse [英] Unable to intercept and manipulate HttpServletResponse in Spring Boot

查看:2257
本文介绍了无法在Spring Boot中拦截和操作HttpServletResponse的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要求 Base64 解码我的Spring Boot服务收到的每个JSON请求负载。在使用HTTP POST 方法发布之前,JSON有效负载将在客户端编码 Base64 。此外,我还需要 Base64 在呈现给调用客户端应用程序之前对JSON响应进行编码。

I have a requirement to Base64 decode every JSON request payload that my Spring Boot service receives. The JSON payload would have been Base64 encoded at the client before posting using the HTTP POST method. Further, I also need to Base64 encode the JSON response before presenting to the calling client application.

我是必需的通过使用处理程序拦截器来减少样板代码。
我已经通过使用拦截器实现了操作的请求/传入支路,但尚未实现响应支路。
我已在下面发布了代码段。截取响应的代码和base64编码它是在拦截器类的postHandle方法中。

I am required to reduce boilerplate code by using handler interceptors. I have already achieved the request/incoming leg of the operation by the use of interceptors but is yet to achieve this for the response leg. I have posted the code snippets below. The code to intercept the response and base64 encode it is in the postHandle method of the interceptor class.

我在这里做错了什么?

拦截器类:

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类:

JavaConfig Class where Interceptor is registered:

@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());
      }

}

控制器类,其中拦截器使用(此处仅显示工作请求分支):

The Controller Class, where the Interceptor is used (Only the working request leg is shown here):

@Autowired
HttpServletRequest request;

String decodedRequest = null;

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

postHandle 方法不起作用。它是 HttpServletResponse null 或者我得到一条异常消息:

The code in the postHandle method does not work. It is either the HttpServletResponse is null or I get an exception message:


已经调用getOutputStream

getOutputStream already called

更新:解决方案以解决响应问题直接在ResponseBodyAdvice
在Controller类中,我添加了以下内容:

Update: Work around solution to reading the response directly in the ResponseBodyAdvice In the Controller Class, I added the following:

@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 <中有以下内容/ strong>

I have the following in the 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文档声明:


postHandle 的方法> HandlerInterceptor 并不总是理想的
适合与 @ResponseBody ResponseEntity 方法。 在这样的
情况下, HttpMessageConverter postHandle 之前写入并提交响应
被调用,这使得无法更改
响应
,例如添加标题。相反,应用程序可以
实现 ResponseBodyAdvice 并将其声明为
@ControllerAdvice bean或直接在
上配置它 RequestMappingHandlerAdapter

the postHandle method of HandlerInterceptor is not always ideally suited for use with @ResponseBody and ResponseEntity methods. In such cases an HttpMessageConverter writes to and commits the response before postHandle is called which makes it impossible to change the response, for example to add a header. Instead an application can implement ResponseBodyAdvice and either declare it as an @ControllerAdvice bean or configure it directly on RequestMappingHandlerAdapter.

随着说:


我在这里做错了什么?

What am I doing wrong here?

由于响应已提交,因此无法更改。要更改响应,您应该注册 ResponseBodyAdvice< T> 并将响应编码逻辑放在那里:

Since the response has been already committed, you can't change it. In order to change the response you should register a ResponseBodyAdvice<T> and put your response encoding logic there:

@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
    }
}

这篇关于无法在Spring Boot中拦截和操作HttpServletResponse的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆