Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring MVC 3.2 @ResponseBody interceptor

In our application we are using JSON for request and response. The controller methods are annotated with @RequestBody(). The object being returned e.g. TransferResponse. I would like to get hold of this object form the @ResponseBody. I have setup a interceptor postHandle method:

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

    {
....

}

So how do I get the JSON in this postHandle method?

Thanks in advance GM

like image 323
user2279337 Avatar asked Jun 24 '13 16:06

user2279337


2 Answers

As Pavel Horal already mentioned, when postHandle() method is called, the response body object is already converted to JSON and written to response. You can try to write your own custom annotation and aspect in order to intercept the controller response body objects.

// 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;
    }
}

Enable support for AspectJ aspects using @EnableAspectJAutoProxy

like image 126
Andrei Zagorneanu Avatar answered Oct 15 '22 05:10

Andrei Zagorneanu


I finally have a working (but not elegant) solution for this case. I think that can have a better solution, but i can't find that.

At first, i was created a Request and Response wrapper that encapsulates the payload, making my request Input Stream and response Output Stream reutilizable and overridable. I need to use this in my Filter to manipulate both the request and the response payloads.

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

    }

}
like image 35
caioquirino Avatar answered Oct 15 '22 07:10

caioquirino