Question Intercepteur Spring MVC 3.2 @ResponseBody


Dans notre application, nous utilisons JSON pour la demande et la réponse. Les méthodes du contrôleur sont annotées avec @RequestBody (). L'objet étant retourné, par ex. TransferResponse. Je voudrais mettre la main sur cet objet du @ResponseBody. J'ai configuré une méthode postHandle d'intercepteur:

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

    {
....

}

Alors, comment puis-je obtenir le JSON dans cette méthode postHandle?

Merci d'avance GM


11
2018-06-24 16:40


origine


Réponses:


Comme Pavel Horal déjà mentionné, quand postHandle() méthode est appelée, l'objet corps de réponse est déjà converti en JSON et écrit dans la réponse. Vous pouvez essayer d'écrire votre propre annotation et aspect personnalisé afin d'intercepter les objets du corps de la réponse du contrôleur.

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

Activer la prise en charge des aspects AspectJ en utilisant @EnableAspectJAutoProxy


6
2018-06-24 18:04



J'ai enfin une solution de travail (mais pas élégante) pour ce cas. Je pense que cela peut avoir une meilleure solution, mais je ne le trouve pas.

Au début, j'ai créé un wrapper Request and Response qui encapsule les données utiles, ce qui rend mon flux de données d'entrée et le flux de sortie de réponse réutilisables et remplaçables. Je dois l'utiliser dans mon filtre pour manipuler les charges utiles de la requête et de la réponse.

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

    }

}

1
2018-04-02 22:38



Comme l’a dit Pavel, vous ne pourrez probablement pas accéder à la réponse JSON de cette façon. Je pense que le meilleur pari est de mettre en œuvre un Filtre qui examine la réponse avant de l'écrire au client. Jettes un coup d'oeil à OncePerRequestFilter pour un point de départ.


0
2018-06-24 18:02



Depuis que la question a été postée, ResponseBodyAdvice a été ajouté dans Spring MVC 4.1. Cette interface permet aux applications de modifier ou de changer complètement le corps avant l’application du convertisseur. le documentation pour intercepter les requêtes a également été mis à jour spécifiquement pour ce problème:

Notez que la méthode postHandle de HandlerInterceptor n'est pas toujours idéale pour être utilisée avec les méthodes @ResponseBody et ResponseEntity. Dans de tels cas, un HttpMessageConverter écrit et valide la réponse avant l'appel de postHandle, ce qui rend impossible la modification de la réponse, par exemple pour ajouter un en-tête. Au lieu de cela, une application peut implémenter ResponseBodyAdvice et la déclarer en tant que bean @ControllerAdvice ou la configurer directement sur RequestMappingHandlerAdapter.


0
2018-02-14 11:56