2017-08-16 64 views
1

在我的项目中,我有一组api调用,它们应该通过某些常用验证集进行过滤。在这种情况下,我必须在请求通过REST控制器之前拦截请求,读取请求体,执行验证并在请求通过验证时将其传递给控制器​​。在使用HttpServletRequestWrapper复制副本后,缺少所需的请求主体

由于HttpServletRequest不能被反序列化多次,所以我使用HttpServletRequestWrapper复制了实际的请求。使用它的副本,我做了验证。

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

public class InterceptorConfig extends WebMvcConfigurerAdapter { 

    @Autowired  
    CustomInterceptor customInterceptor; 

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

    } 
} 

这里是我的内部延伸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(); 
      } 
     }; 
    } 

} 

所有设置现在CustomInterceptorpreHandle方法。现在,当我向/signup/**模式的任何url发送请求时,所有验证都正常发生。但是,一旦请求触及控制器方法,就会弹出错误消息,说明请求主体不可用。

必需的请求体丢失:公共 com.mypackage.myResponseObject com.mypackage.myController.myControllerMethod(com.mypackage.myDTO)

我苦苦寻找这样做的原因和也是解决问题的一种方法。有什么我在RequestWrapper类中做错了吗?或缺少什么?

帮我把这个东西排序出来。

谢谢!

+3

你是不读的副本,你正在阅读的实际要求来使用此过滤器。如果你想要这样做,你需要在一个过滤器中做到这一点,并传递包装的请求。目前该请求仍然被消耗。 –

回答

1

问题似乎是,您正在使用Interceptor读取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

+0

据我所知,我目前正在做的是,制作实际请求的副本并使用该副本进行验证。返回true的最后一个return语句会将实际的请求传递给控制器​​方法。 – vigamage

+0

在您发布的答案中,wt前进是复制的请求。不是吗?没有一种方法可以将复制的东西用于我们的验证和控制器的实际请求。 – vigamage

+0

正如我所知,这是不可能的。因为您阅读实际请求和流的InputStream是一种方法。 – shazin