2016-01-15 225 views
8

我試圖解決一些RequestMapping方法的某些參數,從請求主體中提取值並驗證它們並將它們注入特定的註釋參數中。在Spring的HandlerMethodArgumentResolver中,我如何多次讀取請求體?

@Override 
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, 
           NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { 
    // 1, get corresponding input parameter from NativeWebRequest 
    // 2, validate 
    // 3, type convertion and assemble value to return 
    return null; 
} 

最大的問題是,我發現,HttpServletRequest(從NativeWebRequest獲得)無法讀取輸入流(一些參數是在請求體)一次以上。那麼我怎樣才能多次檢索Inputstream/Reader或請求主體?

+0

一個解決方案可能是使用ThreadLocal將請求中的參數存儲在過濾器中,然後在代碼中的任意位置在任意時間使用它們。 –

+0

@SandeepPoonia這可能有幫助。但是一個問題是,如果我將主體保存到threadlocal中(通過調用HttpServletRequest.getReader/getInputStream),將永遠不會再調用它。在控制器層的事件中,我無法聲明「@RequestBody String body」(它可能會在Spring中引發異常),因爲Spring無法再讀取輸入流。 – Kim

回答

16

您可以添加一個篩選器,攔截當前的HttpServletRequest並將其包裝在自定義HttpServletRequestWrapper中。在您的自定義HttpServletRequestWrapper中,您讀取請求正文並將其緩存,然後執行getInputStreamgetReader以從緩存值中讀取數據。由於包裝的請求後,緩存的值是始終存在的,你可以讀取請求體多次:

@Component 
public class CachingRequestBodyFilter extends GenericFilterBean { 
    @Override 
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) 
      throws IOException, ServletException { 
     HttpServletRequest currentRequest = (HttpServletRequest) servletRequest; 
     MultipleReadHttpRequest wrappedRequest = new MultipleReadHttpRequest(currentRequest); 
     chain.doFilter(wrappedRequest, servletResponse); 
    } 
} 

此過濾器後,大家會看到它有被多次讀取的能力wrappedRequest

public class MultipleReadHttpRequest extends HttpServletRequestWrapper { 
    private ByteArrayOutputStream cachedContent; 

    public MultipleReadHttpRequest(HttpServletRequest request) throws IOException { 
     // Read the request body and populate the cachedContent 
    } 

    @Override 
    public ServletInputStream getInputStream() throws IOException { 
     // Create input stream from cachedContent 
     // and return it 
    } 

    @Override 
    public BufferedReader getReader() throws IOException { 
     // Create a reader from cachedContent 
     // and return it 
    } 
} 

爲了實現MultipleReadHttpRequest,你可以看看spring框架中的ContentCachingRequestWrapper這基本上是一樣的東西。

這種方法有它自己的缺點。首先,它的效率有點低,因爲對於每一個請求,請求體至少被讀取兩次。另一個重要的缺點是,如果您的請求主體包含10 GB價值流,則您會讀取10 GB數據,甚至更糟地將其帶入內存中以供進一步檢查。

+1

是的,這是迄今爲止最好的解決方案。但效率確實是一個大問題。我想我必須找到一種方法來限制輸入流的大小,以防任何人進行破壞或其他事情。至於請求主體至少讀兩次,我認爲這是可以接受的,因爲我只接受來自請求主體的json格式的輸入字符串,通常小於1kb/pr。 – Kim

+1

完整的示例可用於此答案:https://stackoverflow.com/a/17129256/364401 – Stim

相關問題