How to modify request body before reaching controller in spring boot

后端 未结 5 1030
野的像风
野的像风 2020-12-08 12:11

I have a spring boot application. I change the request body of every post request. Is it possible to modify the request body before the request reaches the controller. Pleas

相关标签:
5条回答
  • 2020-12-08 12:47

    Short Answer
    Yes, but not easily.

    Details
    I know of three options to change the body of a request "before" it arrives at the handler method in the controller;

    1. Use AOP to change the request before the method is called.
    2. Create an HTTP filter.
    3. Create a custom Spring HandlerInterceptor.

    Since you are already using spring-boot, option 3, custom Spring HandlerInterceptor, seems like the best option for you.

    Here is a link to a Baeldung Article covering spring HandlerInterceptors.

    The Baeldung article is not the full answer for your problem because you can only read the InputStrem returned by HttpServletRequest one time.

    You will need to create a wrapper class that extends HttpServletRequest and wrap every request in your wrapper class within your custom HandlerInterceptor or in a custom Filter (Filter might be the way to go here).

    Wrapper Class

    1. Read the HttpServletRequest InputStream in the wrapper class constructor
    2. Modify the body per your requirements.
    3. Write the modified body to a ByteArrayOutputStream.
    4. Use toByteArray to retrieve the actual byte[] from the stream.
    5. Close the ByteArrayOutputStream (try-with-resources is good for this).
    6. Override the getInputStream method.
    7. Wrap the byte[] in a ByteArrayInputStream every time the getInputStream is called. Return this stream.

    How To Wrap the Request

    1. In your Filter, instantiate your wrapper class and pass in the original request (which is a parameter to the doFilter method).
    2. Pass the wrapper to the chain.doFilter method (not the original request).
    0 讨论(0)
  • 2020-12-08 12:52

    One way to this is by reflection. ProceedingJoinPoint contains the args object passed to method

    @Aspect
    @Component
    public class AopInterceptor {
    
        @Around(value = "@annotation(xyz.rpolnx.spring.web.poc.annotation.AopIntercepExample)")
        public Object handler(final ProceedingJoinPoint joinPoint) throws Throwable {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    
            Object[] args = joinPoint.getArgs();
    
            Class<?> someClass = args[0].getClass();
    
            Field field = someClass.getDeclaredField("custom");
            field.setAccessible(true);
            field.set(args[0], "custom");
            field.setAccessible(false);
    
            return joinPoint.proceed();
        }
    }
    
    @RestController
    public class SimpleController {
    
        @PostMapping("/aop")
        @AopIntercepExample
        public Person handleAopIntercept(@RequestBody Person nodes) {
            return nodes;
        }
    }
    
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AopIntercepExample {
    }
    
    public class Person {
        private String name;
        private String id;
        private String custom;
    }
    
    0 讨论(0)
  • 2020-12-08 12:53

    Another alternative would be adding an attribute to the HttpServletRequest object. And after that you can read that attribute in the Controller class with @RequestAttribute annotation.

    In the Interceptor

    @Component
        public class SimpleInterceptor extends HandlerInterceptorAdapter {
    
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                    throws ServletException, IOException {
                String parameter = request.getParameter("parameter");
                if (parameter == "somevalue") {
                    request.setAttribute("customAttribute", "value");
                }
    
                return true;
            }
    
        }
    

    In the Controller

    @RestController
    @RequestMapping("")
    public class SampleController {
    
    
        @RequestMapping(value = "/sample",method = RequestMethod.POST)
        public String work(@RequestBody SampleRequest sampleRequest, @RequestAttribute("customAttribute") String customAttribute) {
            System.out.println(customAttribute);
            return "This works";
        }
    }
    

    This has advantage of not modifying the request body.

    0 讨论(0)
  • 2020-12-08 12:58

    Here's how I achieved it using the RequestBodyAdvice:

    1. Create a class that implements RequestBodyAdvice and annotate it with @ControllerAdvice
    @ControllerAdvice
    public class CustomRequestBodyAdvice implements RequestBodyAdvice {
    
    1. You will have to implements 4 methods:

    a. support: here, you can control which controller you are targeting, and better which request body by specifying the type of the request body

    @Override
        public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
            log.info("In supports() method of {}", getClass().getSimpleName());
            return methodParameter.getContainingClass() == AuthorController.class && type.getTypeName() == AuthorDTO.class.getTypeName();
        }
    

    b. beforeBodyReady

    <!-- language: lang-js -->
    
    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        log.info("In beforeBodyRead() method of {}", getClass().getSimpleName());
        return httpInputMessage;
    }
    

    c. afterBodyRead: here it is where you can modify the request body

    <!-- language: lang-js -->
    
    @Override
    public Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        log.info("In afterBodyRead() method of {}", getClass().getSimpleName());
        if (body instanceof AuthorDTO) {
            AuthorDTO authorDTO = (AuthorDTO) body;
            authorDTO.setName("Test");
            return authorDTO;
        }
    
        return body;
    }
    

    d. handleEmptyBody

    <!-- language: lang-js -->
    
    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        log.info("In handleEmptyBody() method of {}", getClass().getSimpleName());
        return body;
    }
    

    Source: http://www.javabyexamples.com/quick-guide-to-requestbodyadvice-in-spring-mvc

    0 讨论(0)
  • 2020-12-08 13:02

    My answer using HTTP Filter.

    RequestFilter.java

    @Component
    public class RequestFilter implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            RequestWrapper wrappedRequest = new RequestWrapper((HttpServletRequest) request);
            chain.doFilter(wrappedRequest, response);
        }
    
        @Override
        public void destroy() {
    
        }
    
    }
    

    RequestWrapper.java

     public class RequestWrapper extends HttpServletRequestWrapper {
            private final String body;
            private ObjectMapper objectMapper = new ObjectMapper();
        
            public RequestWrapper(HttpServletRequest request) throws IOException {
                // So that other request method behave just like before
                super(request);
        
                StringBuilder stringBuilder = new StringBuilder();
                BufferedReader bufferedReader = null;
                try {
                    InputStream inputStream = request.getInputStream();
                    if (inputStream != null) {
                        bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                        char[] charBuffer = new char[128];
                        int bytesRead = -1;
                        while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                            stringBuilder.append(charBuffer, 0, bytesRead);
                        }
        
                    } else {
                        stringBuilder.append("");
                    }
                } catch (IOException ex) {
                    throw ex;
                } finally {
                    if (bufferedReader != null) {
                        try {
                            bufferedReader.close();
                        } catch (IOException ex) {
                            throw ex;
                        }
                    }
                }
                // Store request body content in 'requestBody' variable
                String requestBody = stringBuilder.toString();
                JsonNode jsonNode = objectMapper.readTree(requestBody);
                //TODO -- Update your request body here 
                //Sample
                ((ObjectNode) jsonNode).remove("key");
                // Finally store updated request body content in 'body' variable
                body = jsonNode.toString();
        
            }
        
            @Override
            public ServletInputStream getInputStream() throws IOException {
                final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
                ServletInputStream servletInputStream = new ServletInputStream() {
                    public int read() throws IOException {
                        return byteArrayInputStream.read();
                    }
        
                    @Override
                    public boolean isFinished() {
                        return false;
                    }
        
                    @Override
                    public boolean isReady() {
                        return false;
                    }
        
                    @Override
                    public void setReadListener(ReadListener listener) {
        
                    }
                };
                return servletInputStream;
            }
        
            @Override
            public BufferedReader getReader() throws IOException {
                return new BufferedReader(new InputStreamReader(this.getInputStream()));
            }
    }
    
    0 讨论(0)
提交回复
热议问题