Java 如何在spring boot中到达控制器之前修改请求正文

Java 如何在spring boot中到达控制器之前修改请求正文,java,spring,spring-boot,Java,Spring,Spring Boot,我有一个spring启动应用程序。 我更改每个post请求的请求正文。 是否可以在请求到达控制器之前修改请求正文。 请提供一个例子。简短回答 是的,但不容易 详细信息 我知道更改请求正文的三个选项 “before”到达控制器中的handler方法 在调用方法之前,使用AOP更改请求 创建HTTP筛选器 创建自定义Spring HandlerInterceptor 由于您已经在使用spring boot, 选项3,定制弹簧手柄拦截器, 似乎是你最好的选择 这里是一个到覆盖弹簧手柄拦截器的链接 Ba

我有一个spring启动应用程序。 我更改每个post请求的请求正文。 是否可以在请求到达控制器之前修改请求正文。
请提供一个例子。

简短回答
是的,但不容易

详细信息
我知道更改请求正文的三个选项 “before”到达控制器中的handler方法

  • 在调用方法之前,使用AOP更改请求
  • 创建HTTP筛选器
  • 创建自定义Spring HandlerInterceptor
  • 由于您已经在使用spring boot, 选项3,定制弹簧手柄拦截器, 似乎是你最好的选择

    这里是一个到覆盖弹簧手柄拦截器的链接

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

    您需要创建一个扩展
    HttpServletRequest
    并将包装类中的每个请求包装在自定义HandlerInterceptor中,或包装在自定义筛选器中(这里可能是使用筛选器)

    包装类

  • 阅读包装类构造函数中的
    HttpServletRequest
    InputStream
  • 根据您的要求修改主体
  • 通过tearrayoutputstream将修改后的正文写入
  • 使用
    toByteArray
    从流中检索实际的
    字节[]
  • 关闭ByteArrayOutputStream(使用资源进行尝试有助于此)
  • 重写
    getInputStream
    方法
  • 每次调用
    getInputStream
    时,将
    字节[]
    包装在ByteArrayInputStream中。返回此流
  • 如何包装请求

  • 在过滤器中,实例化包装器类并传入原始请求(这是doFilter方法的一个参数)
  • 将包装传递给chain.doFilter方法(而不是原始请求)

  • 另一种选择是向HttpServletRequest对象添加一个属性。之后,您可以使用@RequestAttribute注释在控制器类中读取该属性

    在拦截器中

    @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;
            }
    
        }
    
    在控制器中

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

    这样做的优点是不修改请求主体

    我的答案使用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() {
    
        }
    
    }
    
     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()));
            }
    }
    
    RequestWrapper.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() {
    
        }
    
    }
    
     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()));
            }
    }
    

    一种方法是通过反思。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;
    }
    
    @方面
    @组成部分
    公共类AOP接收器{
    @大约(value=“@annotation(xyz.rpolnx.spring.web.poc.annotation.AopIntercepExample)”)
    公共对象处理程序(最终处理joinPoint joinPoint)抛出可丢弃的{
    HttpServletRequest请求=((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
    对象[]args=joinPoint.getArgs();
    类someClass=args[0]。getClass();
    Field=someClass.getDeclaredField(“自定义”);
    字段。setAccessible(true);
    field.set(参数[0],“自定义”);
    字段.setAccessible(false);
    返回joinPoint.procedure();
    }
    }
    @RestController
    公共类SimpleController{
    @后映射(“/aop”)
    @AopIntercepExample
    公共人员handleaointercept(@RequestBody Person节点){
    返回节点;
    }
    }
    @目标({ElementType.METHOD})
    @保留(RetentionPolicy.RUNTIME)
    public@interface AopIntercepExample{
    }
    公共阶层人士{
    私有字符串名称;
    私有字符串id;
    私人字符串自定义;
    }
    
    以下是我如何使用RequestBodyAdvice实现它的:

  • 创建一个实现RequestBodyAdvice的类,并用@ControllerAdvice对其进行注释
  • @ControllerAdvice
    公共类CustomRequestBodyAdvice实现RequestBodyAdvice{
    
  • 您必须实现4种方法:
  • a、 支持:在这里,您可以通过指定请求主体的类型来控制目标控制器,更好地控制请求主体

    <!-- 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;
    }
    
    @覆盖
    公共布尔支持(MethodParameter、类型、类>aClass){
    info(“在{}的supports()方法中”,getClass().getSimpleName());
    返回methodParameter.getContainingClass()==AuthorController.class&&type.getTypeName()==AuthorDTO.class.getTypeName();
    }
    
    b、 在身体准备好之前

    <!-- 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;
    }
    
    
    @凌驾
    公共HttpInputMessage beforeBodyRead(HttpInputMessage HttpInputMessage,MethodParameter MethodParameter,类型,类>aClass)引发IOException{
    info(“在{}的beforeBodyRead()方法中,getClass().getSimpleName());
    返回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;
    }
    
    
    @凌驾
    公共对象afterBodyRead(对象主体、HttpInputMessage HttpInputMessage、MethodParameter方法参数、类型、类>aClass){
    info(“在{}的afterBodyRead()方法中,getClass().getSimpleName());
    if(AuthorDTO的主体实例){
    AuthorDTO AuthorDTO=(AuthorDTO)body;
    authorDTO.setName(“测试”);
    返回authordo;
    }
    返回体;
    }
    
    d、 手持式车身

    <!-- 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;
    }
    
    
    @凌驾
    公共对象handleEmptyBody(对象体,HttpInputMessage HttpInputMessage,MethodParameter MethodParameter,类型,类>aClass){
    info(“在{}的handleEmptyBody()方法中”,getClass().getSimpleName());
    返回体;
    }
    

    来源:

    看看这个:我认为只允许设置请求参数zombie。在我的例子中,修改请求正文。谢谢,我将工作并更新状态HI its TROWS无法读取HTTP消息:org.springframework.HTTP.converter.httpMessageNodeTableException:读取输入消息时发生I/O错误;嵌套异常为java.io.I