2017-08-16 63 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