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

问题描述 投票:0回答:2

我正在尝试解析

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;
}

最大的问题是我发现

HttpServletRequest
(从
NativeWebRequest
获取)无法读取输入流(某些参数在请求体中)不止一次。那么如何多次检索
Inputstream
/
Reader
或请求正文?

java spring spring-mvc
2个回答
41
投票

您可以添加过滤器,拦截当前的

HttpServletRequest
并将其包装在自定义的
HttpServletRequestWrapper
中。在您的自定义
HttpServletRequestWrapper
中,您读取请求正文并将其缓存,然后实现
getInputStream
getReader
以从缓存的值中读取。由于包装请求后,缓存的值始终存在,因此您可以多次读取请求正文:

@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
数据,甚至更糟糕的是,将其放入内存中以供进一步检查。


0
投票

为什么不直接将第一次读取的值设置为 HTTP servlet 请求的属性呢?然后,您下次只需访问此属性,而不是再次尝试从 HTTP servlet 请求中读取值:

@Override
public Object resolveArgument(final MethodParameter parameter,
                              final ModelAndViewContainer mavContainer,
                              final NativeWebRequest webRequest,  
                              final WebDataBinderFactory binderFactory) throws Exception {

   HttpServletRequest httpServletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
   MyContent content = (MyContent) httpServletRequest.getAttribute("MY_ATTRIBUTE_KEY");
   if (content == null) {
      // will be executed only the first time
      content = readContentFromWebRequest(httpServletRequest);
      httpServletRequest.setAttribute("MY_ATTRIBUTE_KEY", content);
   }

   // do what you need to do with the content, the first time and every other times
}
© www.soinside.com 2019 - 2024. All rights reserved.