【问题标题】:Spring MVC 3.2 @ResponseBody interceptorSpring MVC 3.2 @ResponseBody 拦截器
【发布时间】:2013-06-21 06:17:58
【问题描述】:

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

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

    {
....

}

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

提前致谢 通用汽车

【问题讨论】:

  • 这个不错。我不认为你可以在控制返回到DispatcherServlet(负责调用处理程序拦截器)之前处理@ResponseBody 对象(编组到servlet 的响应输出流中)。根据您的用例,您需要找到不同的方法。

标签: spring spring-mvc


【解决方案1】:

正如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 方面的支持

【讨论】:

  • 谢谢大家,我会试试的。
【解决方案2】:

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

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

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

    }

}

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      就像 Pavel 所说,您可能无法通过这种方式获取响应 JSON。我认为最好的办法是实现Filter,在将响应写入客户端之前查看响应。以OncePerRequestFilter 为起点。

      【讨论】:

      • 谢谢大家,我会试试的。
      猜你喜欢
      • 2012-09-02
      • 2014-05-19
      • 1970-01-01
      • 2014-10-22
      • 2013-05-19
      • 2023-03-17
      • 2011-12-24
      • 1970-01-01
      • 2018-08-09
      相关资源
      最近更新 更多