使用 HttpServletRequestWrapper 制作副本后缺少所需的请求正文

Posted

技术标签:

【中文标题】使用 HttpServletRequestWrapper 制作副本后缺少所需的请求正文【英文标题】:Required request body is missing after making a copy using HttpServletRequestWrapper 【发布时间】:2018-01-24 02:13:45 【问题描述】:

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

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

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

public class InterceptorConfig extends WebMvcConfigurerAdapter 

     @Autowired     
     CustomInterceptor customInterceptor;

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

        

这是我在 CustomInterceptor 类中的 preHandle 方法,它扩展了 HandlerInterceptorAdaptor

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

这是我的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();
            
        ;
    


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

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

我正在努力寻找造成这种情况的原因以及解决问题的方法。我在 RequestWrapper 类中有什么做错了吗?还是缺少什​​么?

帮我解决这个问题。

谢谢!

【问题讨论】:

您不是在阅读副本,而是在阅读实际请求。如果您希望它工作,您需要在过滤器中执行此操作并传递包装的请求。目前该请求仍在消耗中。 【参考方案1】:

问题似乎是您正在使用拦截器来读取HttpServletRequestInputStream 并将其包装在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);
    

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

【讨论】:

据我了解,我目前正在做的是制作实际请求的副本并使用该副本进行验证。最后一个返回 true 的 return 语句会将实际请求传递给控制器​​方法。 在您发布的答案中,wt 前进是复制的请求。不是吗?没有办法将复制的东西用于我们的验证和对控制器东西的实际请求。 据我所知,这是不可能的。因为您阅读了实际 Request 的 InputStream,而 Streams 是一种方式。 我按照你说的方式实现了。现在我面临的问题是将其配置为仅对某些请求启用。 url 过滤是现在的问题 嗨,我按照你在这里的指示做,但我的 RestController 中仍然有一个 Required request body is missing: @RequestBody

以上是关于使用 HttpServletRequestWrapper 制作副本后缺少所需的请求正文的主要内容,如果未能解决你的问题,请参考以下文章

第一篇 用于测试使用

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份