【问题标题】:How to getInputStream / getParameter of an HttpServletRequest in a filter without consuming it?如何在不使用过滤器的情况下获取 HttpServletRequest 的输入流/获取参数?
【发布时间】:2016-02-25 10:33:37
【问题描述】:

有没有办法在过滤器中读取您的请求正文而不会破坏您的控制器?

即:

  • 可以使用@RequestParamgetParameter(String name) 方法获取参数
  • 即使您已经在过滤器中需要它,也可以在控制器中获取请求的 InputStream。

【问题讨论】:

    标签: servlets servlet-filters query-parameters


    【解决方案1】:

    我的灵感来自这篇文章:Get the POST request body from HttpServletRequest:

    它复制然后缓存正文,然后提取参数映射并将其存储在属性formParameters中。

    public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
    
        private final FormHttpMessageConverter formConverter = new AllEncompassingFormHttpMessageConverter();
        MultiValueMap<String, String> formParameters;
        private ByteArrayOutputStream cachedBytes;
    
        public MultiReadHttpServletRequest(HttpServletRequest request) throws IOException {
            super(request);
            HttpInputMessage inputMessage = new ServletServerHttpRequest(this) {
                @Override
                public InputStream getBody() throws IOException {
                    return getInputStream();
                }
            };
            formParameters = formConverter.read(null, inputMessage);
        }
    
        @Override
        public ServletInputStream getInputStream() throws IOException {
            if (cachedBytes == null)
                cacheInputStream();
    
            return new CachedServletInputStream();
        }
    
        @Override
        public BufferedReader getReader() throws IOException{
            return new BufferedReader(new InputStreamReader(getInputStream()));
        }
    
        @Override
        public String getParameter(String name) {
            String queryStringValue = super.getParameter(name);
            String formValue = this.formParameters.getFirst(name);
            return (queryStringValue != null) ?  queryStringValue : formValue;
        }
    
        @Override
        public Map<String, String[]> getParameterMap() {
            Map<String, String[]> result = new LinkedHashMap<>();
            Enumeration<String> names = this.getParameterNames();
            while (names.hasMoreElements()) {
                String name = names.nextElement();
                result.put(name, this.getParameterValues(name));
            }
            return result;
        }
    
        @Override
        public Enumeration<String> getParameterNames() {
            Set<String> names = new LinkedHashSet<>();
            names.addAll(Collections.list(super.getParameterNames()));
            names.addAll(this.formParameters.keySet());
            return Collections.enumeration(names);
        }
    
        @Override
        public String[] getParameterValues(String name) {
            String[] queryStringValues = super.getParameterValues(name);
            List<String> formValues = this.formParameters.get(name);
            if (formValues == null) {
                return queryStringValues;
            }
            else if (queryStringValues == null) {
                return formValues.toArray(new String[formValues.size()]);
            }
            else {
                List<String> result = new ArrayList<>();
                result.addAll(Arrays.asList(queryStringValues));
                result.addAll(formValues);
                return result.toArray(new String[result.size()]);
            }
        }
    
        private void cacheInputStream() throws IOException {
    /* Cache the inputstream in order to read it multiple times. For
     * convenience, I use apache.commons IOUtils
     */
            cachedBytes = new ByteArrayOutputStream();
            IOUtils.copy(super.getInputStream(), cachedBytes);
        }
    
        /* An inputstream which reads the cached request body */
        public class CachedServletInputStream extends ServletInputStream {
            private ByteArrayInputStream input;
    
            public CachedServletInputStream() {
      /* create a new input stream from the cached request body */
                input = new ByteArrayInputStream(cachedBytes.toByteArray());
            }
    
            @Override
            public int read() throws IOException {
                return input.read();
            }
        }
    }
    

    【讨论】:

    • 将整个请求数据缓存在内存中。对于文件上传之类的事情,您可能不想这样做。
    • 你的意思是在 formParameters 中?
    • 我的意思是cachedBytes 可能很大。
    • 是的,我想知道它是否有那么大,因为最初它已经在某个地方,可以通过“request.getInputStream()”访问。那么,考虑到原始对象已被消耗,我们最终是否只使用IOUtils.copy(super.getInputStream(), cachedBytes); 移动此对象?
    • request.getInputStream 最初在接收请求时从请求中读取数据。这可用于流式传输大数据,例如文件上传,而无需一次将它们全部读入内存。就像您可以使用 FileInputStream 从磁盘读取千兆字节数据一样,即使它可能不适合内存。
    猜你喜欢
    • 2013-01-24
    • 1970-01-01
    • 2021-08-02
    • 2011-02-10
    • 1970-01-01
    • 2022-12-03
    • 2021-05-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多