首页 文章

Spring MVC 3.2 @ResponseBody拦截器

提问于
浏览
11

在我们的应用程序中,我们使用JSON进行请求和响应 . 控制器方法使用@RequestBody()进行注释 . 正在返回的对象,例如TransferResponse . 我想从@ResponseBody获取此对象 . 我已经设置了拦截器postHandle方法:

@Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object handler,
                           ModelAndView modelAndView) throws java.lang.Exception

    {
....

}

那么如何在这个postHandle方法中获取JSON呢?

在此先感谢GM

4 回答

  • 6

    正如Pavel Horal已经提到的那样,当调用 postHandle() 方法时,响应体对象已经转换为JSON并写入响应 . 您可以尝试编写自己的自定义注释和方面,以拦截控制器响应主体对象 .

    // custom annotation
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyCustomAnnotation {
    }
    
    // aspect
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class MyCustomAnnotationAspect {
        @Around(value = "@annotation(org.package.MyCustomAnnotation)", argNames = "pjp")
        public Object aroundAdvice(final ProceedingJoinPoint pjp) {
            // this is your response body
            Object responseBody = pjp.proceed();
            return responseBody;
        }
    }
    

    使用 @EnableAspectJAutoProxy 启用对AspectJ方面的支持

  • 1

    对于这种情况,我终于有一个工作(但不优雅)的解决方案 . 我认为可以有更好的解决方案,但我找不到 .

    起初,我创建了一个请求和响应包装器,它封装了有效负载,使我的请求输入流和响应输出流可重用和可覆盖 . 我需要在我的过滤器中使用它来操作请求和响应有效负载 .

    import java.io.BufferedReader;
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletInputStream;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpServletResponseWrapper;
    
    import org.springframework.context.ApplicationContext;
    
    import br.com.vivo.core.controller.impl.utils.ApplicationContextUtils;
    
    import com.fasterxml.jackson.core.JsonFactory;
    import com.fasterxml.jackson.core.JsonParser;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.node.ObjectNode;
    
    @WebFilter(urlPatterns = { "/*" })
    public class HeadBodyFilter implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
    
            ApplicationContext applicationContext = ApplicationContextUtils.getApplicationContext();
    
            ObjectMapper objectMapper = (ObjectMapper) applicationContext.getBean("jacksonObjectMapper");
            JsonFactory jsonFactory = objectMapper.getFactory();
    
            ByteResponseWrapper byteResponseWrapper = new ByteResponseWrapper((HttpServletResponse) response);
            ByteRequestWrapper byteRequestWrapper = new ByteRequestWrapper((HttpServletRequest) request);
    
    
            String jsonRequestString = new String(byteRequestWrapper.getBytes());
            JsonParser requestParser = jsonFactory.createParser(jsonRequestString);
            JsonNode rootRequestNode = objectMapper.readTree(requestParser);
    
    
            if(rootRequestNode != null && rootRequestNode.has("body")) {
                JsonNode requestBody = rootRequestNode.get("body");
                writeJsonIntoRequest(byteRequestWrapper, requestBody, objectMapper);
            }
    
    
            chain.doFilter(byteRequestWrapper, byteResponseWrapper);
    
            String jsonResponseString = new String(byteResponseWrapper.getBytes(), response.getCharacterEncoding());
    
    
    
            JsonParser responseParser = jsonFactory.createParser(jsonResponseString);
            JsonNode rootResponseNode = objectMapper.readTree(responseParser);
    
            Object head = "Whoo hoo!";
    
            ObjectNode responseObjectWrapper = objectMapper.createObjectNode();
            responseObjectWrapper.put("head", objectMapper.valueToTree(head));
            responseObjectWrapper.put("body", rootResponseNode);
    
            writeJsonIntoResponse(response, responseObjectWrapper, objectMapper);
    
        }
    
    
        private void writeJsonIntoRequest(ByteRequestWrapper request,
                JsonNode requestBody, ObjectMapper objectMapper) throws IOException {
    
            String json = objectMapper.writeValueAsString(requestBody);
            request.replaceRequestPayload(json.getBytes());
    
        }
    
        @Override
        public void destroy() {
    
        }
    
    
    
        /**
         * Escreve o json no response
         * 
         * @param response
         * @param rootNode
         * @throws IOException
         */
        private void writeJsonIntoResponse(final ServletResponse response, final JsonNode responseBody, final ObjectMapper objectMapper) throws IOException {
    
            String json = objectMapper.writeValueAsString(responseBody);
    
            // escreve o json
            response.getOutputStream().write((json + "\r\n").getBytes(response.getCharacterEncoding()));
        }
    
    
    
        static class ByteResponseWrapper extends HttpServletResponseWrapper {
    
            private PrintWriter writer;
            private ByteOutputStream output;
    
            public byte[] getBytes() {
                writer.flush();
                return output.getBytes();
            }
    
            public ByteResponseWrapper(HttpServletResponse response) {
                super(response);
                output = new ByteOutputStream();
                writer = new PrintWriter(output);
            }
    
            @Override
            public PrintWriter getWriter() {
                return writer;
            }
    
            @Override
            public ServletOutputStream getOutputStream() throws IOException {
                return output;
            }
        }
    
    
    
        static class ByteRequestWrapper extends HttpServletRequestWrapper {
    
            byte[] requestBytes = null;
            private ByteInputStream byteInputStream;
    
    
            public ByteRequestWrapper(HttpServletRequest request) throws IOException {
                super(request);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
    
                InputStream inputStream = request.getInputStream();
    
                byte[] buffer = new byte[4096];
                int read = 0;
                while ( (read = inputStream.read(buffer)) != -1 ) {
                    baos.write(buffer, 0, read);
                }
    
                replaceRequestPayload(baos.toByteArray());
            }
    
            public byte[] getBytes() {
                return requestBytes;
            }
    
            @Override
            public BufferedReader getReader() throws IOException {
                return new BufferedReader(new InputStreamReader(getInputStream()));
            }
    
            @Override
            public ServletInputStream getInputStream() throws IOException {
                return byteInputStream;
            }
    
            public void replaceRequestPayload(byte[] newPayload) {
                requestBytes = newPayload;
                byteInputStream = new ByteInputStream(new ByteArrayInputStream(requestBytes));
            }
        }
    
        static class ByteOutputStream extends ServletOutputStream {
    
            private ByteArrayOutputStream bos = new ByteArrayOutputStream();
    
            @Override
            public void write(int b) throws IOException {
                bos.write(b);
            }
    
            public byte[] getBytes() {
                return bos.toByteArray();
            }
        }
    
        static class ByteInputStream extends ServletInputStream {
    
            private InputStream inputStream;
    
            public ByteInputStream(final InputStream inputStream) {
                this.inputStream = inputStream;
            }
    
            @Override
            public int read() throws IOException {
                return inputStream.read();
            }
    
        }
    
    }
    
  • 0

    就像Pavel说的那样,你可能无法以这种方式得到JSON的响应 . 我认为最好的办法是实现一个Filter,在将响应写入客户端之前查看响应 . 看一下OncePerRequestFilter的起点 .

  • 0

    自问题发布以来,在Spring MVC 4.1中添加了ResponseBodyAdvice . 在应用转换器之前,此接口允许应用程序更改或完全更改主体 . documentation for intercepting requests也专门针对此问题进行了更新:

    请注意,HandlerInterceptor的postHandle方法并不总是非常适合与@ResponseBody和ResponseEntity方法一起使用 . 在这种情况下,HttpMessageConverter在调用postHandle之前写入并提交响应,这使得无法更改响应,例如添加标头 . 相反,应用程序可以实现ResponseBodyAdvice并将其声明为@ControllerAdvice bean或直接在RequestMappingHandlerAdapter上配置它 .

相关问题