如何在Spring Boot中到达控制器之前修改请求主体

时间:2018-06-19 15:58:33

标签: java spring spring-boot

我有一个Spring Boot应用程序。 我更改每个发布请求的请求正文。 是否可以在请求到达控制器之前修改请求主体。 请提供示例。

5 个答案:

答案 0 :(得分:5)

另一种选择是将属性添加到 HttpServletRequest 对象。然后,您可以使用 @RequestAttribute 批注在Controller类中读取该属性。

在拦截器中

material-design-icons

在控制器中

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

    }

这具有不修改请求正文的优点。

答案 1 :(得分:2)

简短回答
是的,但不容易。

详细信息
我知道三种更改请求内容的选项 “之前”到达控制器中的处理程序方法;

  1. 在调用方法之前,使用AOP更改请求。
  2. 创建一个HTTP过滤器。
  3. 创建一个自定义Spring HandlerInterceptor。

由于您已经在使用spring-boot, 选项3,自定义Spring HandlerInterceptor, 似乎是最适合您的选择。

这是一个指向Baeldung Article的链接,其中涉及弹簧HandlerInterceptor。

Baeldung文章不是您问题的完整答案 因为您一次只能读取InputStrem返回的HttpServletRequest

您将需要创建扩展HttpServletRequest的包装器类 并在自定义HandlerInterceptor内的包装类中包装每个请求。

包装器类

  1. 在包装器类构造函数中读取HttpServletRequest InputStream
  2. 根据您的要求修改车身。
  3. 将修改后的正文写入ByteArrayOutputStream
  4. 使用toByteArray从流中检索实际的byte[]
  5. 关闭ByteArrayOutputStream(try-with-resources很好)。
  6. 覆盖getInputStream方法。
  7. 每次调用byte[]时,将getInputStream包装在ByteArrayInputStream中。返回此流。

答案 2 :(得分:0)

我的回答使用 HTTP过滤器

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

答案 3 :(得分:0)

一种解决方法是反思。 ProceedingJoinPoint包含传递给方法的args对象

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

答案 4 :(得分:-1)

这是我使用RequestBodyAdvice实现它的方式:

  1. 创建一个实现RequestBodyAdvice的类,并使用@ControllerAdvice对其进行注释
@ControllerAdvice
public class CustomRequestBodyAdvice implements RequestBodyAdvice {
  1. 您将必须实现4种方法:

a。支持:在这里,您可以通过指定请求主体的类型来控制要定位的控制器,更好地控制哪个请求主体

@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:在这里您可以修改请求正文

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

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