使用 HttpServletRequestWrapper 进行复制后缺少必需的请求正文

vig*_*age 6 java json spring-mvc httprequest spring-boot

在我的项目中,我有一组 api 调用,应通过某些常见验证集进行过滤。在这种情况下,我必须在请求到达 REST 控制器之前拦截该请求,读取请求正文,进行验证,如果请求通过验证,则将其传递给控制器​​。

由于HttpServletRequest不能多次反序列化,因此我使用 aHttpServletRequestWrapper来复制实际请求。我使用它制作的副本进行验证。

下面是拦截请求的配置类。

public class InterceptorConfig extends WebMvcConfigurerAdapter {

     @Autowired     
     CustomInterceptor customInterceptor;

     @Override  
     public void addInterceptors(InterceptorRegistry registry) { 
        registry.addInterceptor(customInterceptor).addPathPatterns("/signup/**");

     }   
}
Run Code Online (Sandbox Code Playgroud)

这是我在类中扩展的preHandle方法CustomInterceptorHandlerInterceptorAdaptor

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

    ServletRequest copiedRequest = new HttpRequestWrapper(request);

    Map<String, Object> jsonMap = mapper.readValue(copiedRequest.getInputStream(), Map.class);

    if(jsonMap.containsKey("userId")){
        long userId = jsonMap.get("userId");
        MyClass myObject= myAutowiredService.getMyObject(userId);
        if(myObject == null){
            response.setStatus(HttpStatus.SC_NOT_ACCEPTABLE);
            return false;
        } 
        // some more validations which end up returning false if they are met
    }
    return true;
}
Run Code Online (Sandbox Code Playgroud)

这是我的HttpRequestWrapper

public class HttpRequestWrapper extends HttpServletRequestWrapper {

    private byte[] requestBody;

    public HttpRequestWrapper(HttpServletRequest request) throws IOException{
        super(request);

        try {
            requestBody = IOUtils.toByteArray(request.getInputStream());
        } catch (IOException ex) {
            requestBody = new byte[0];
        }
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestBody);

        return new ServletInputStream() {

            @Override
            public boolean isFinished() {
                return byteArrayInputStream.available() == 0;
            }

            @Override
            public boolean isReady() {
                return true;
            }

            @Override
            public void setReadListener(ReadListener listener) {
                throw new RuntimeException("Not implemented");
            }

            public int read () throws IOException {
                return byteArrayInputStream.read();
            }
        };
    }

}
Run Code Online (Sandbox Code Playgroud)

现在一切就绪。现在,当我向任何具有 模式的 url 发送请求时/signup/**,所有验证都会正常进行。但是,一旦请求到达控制器方法,就会弹出错误,指出请求正文不可用。

缺少必需的请求正文:public com.mypackage.myResponseObject com.mypackage.myController.myControllerMethod(com.mypackage.myDTO)

我正在努力寻找其原因以及解决该问题的方法。我在 RequestWrapper 类中做错了什么吗?或者缺少什么?

帮我解决一下这件事。

谢谢!

sha*_*zin 2

问题似乎是您正在使用拦截器来读取HttpServletRequest'sInputStream并将其包装起来HttpRequestWrapper,但包装器永远不会返回。

我认为你应该使用Filter

public class CustomFilter extends OncePerRequestFilter {
    public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        ServletRequest copiedRequest = new HttpRequestWrapper(request);

        Map<String, Object> jsonMap = mapper.readValue(copiedRequest.getInputStream(), Map.class);

        if(jsonMap.containsKey("userId")){
            long userId = jsonMap.get("userId");
            MyClass myObject= myAutowiredService.getMyObject(userId);
            if(myObject == null){
                response.setStatus(HttpStatus.SC_NOT_ACCEPTABLE);
                //return false;
            } 
            // some more validations which end up returning false if they are met
         }

         filterChain.doFilter(copiedRequest, (ServletResponse) response);
    }
}
Run Code Online (Sandbox Code Playgroud)

您需要在或中使用此过滤web.xmlWebApplicationInitializer

  • 您好,我按照您的指示进行操作,但我仍然在带有 @RequestBody 的 RestController 中收到“缺少必需的请求正文:” (2认同)