2016-01-15 221 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

相关问题