如何在Spring'HandlerMethodArgumentResolver'中多次读取请求体?

Kim*_*Kim 16 java spring spring-mvc

我正在尝试解决某些RequestMapping方法的某些参数,从请求体中提取值并验证它们并将它们注入到某些带注释的参数中.

@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;
}
Run Code Online (Sandbox Code Playgroud)

最大的问题是我发现HttpServletRequest(get from NativeWebRequest)无法读取输入流(某些参数在请求体中)不止一次.那么我怎样才能多次检索Inputstream/ Reader或请求体?

Ali*_*ani 24

您可以添加过滤器,拦截当前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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在此过滤器之后,每个人都将看到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
    }
}
Run Code Online (Sandbox Code Playgroud)

为了实现MultipleReadHttpRequest,您可以ContentCachingRequestWrapper从spring框架看一下,它基本上是一样的.

这种方法有其自身的缺点.首先,它效率有点低,因为对于每个请求,请求主体至少被读取两次.另一个重要的缺点是,如果您的请求正文包含10 GB有价值的流,您会读取该10 GB数据,甚至更糟糕的是将其带入内存以供进一步检查.

  • 完整的示例可在以下答案中找到:/sf/answers/1199047951/ (4认同)
  • 是的,这是迄今为止最好的解决方案。但效率确实是一个大问题。我想我必须找到一种方法来限制输入流的大小,以防有人参与破坏或其他事情。至于请求体至少读取两次,我认为这是可以接受的,因为我只接受请求体中的 json 格式的输入字符串,通常小于 1kb/pr。 (2认同)
  • 顺便说一句, ContentCachingRequestWrapper 不做同样的事情,它允许从缓存中获取数据,但您必须首先读取真实数据。它对记录很有用,但对于其他事情不太好。 (2认同)
  • 一个有趣的事实是,由于某种原因,Spring 的 ContentCachingRequestWrapper 并没有真正“缓存”任何东西(正如我通过试验和错误发现的那样)。相反,@Stim 在姊妹线程中引用的解决方案工作得很好 - 该解决方案要求您实现自己的“MultiReadHttpServletRequest”包装器。 (2认同)