zoukankan      html  css  js  c++  java
  • HttpServletRequestWrapper使用技巧(自定义session和缓存InputStream)

    一、前言

      javax.servlet.http.HttpServletRequestWrapper 是一个开发者可以继承的类,我们可以重写相应的方法来实现session的自定义以及缓存InputStream,在程序中可以多次获取request body的内容。

    二、自定义seesion

    import javax.servlet.http.*;
    
    public class CustomizeHttpServletRequest extends HttpServletRequestWrapper {
    
        public CustomizeHttpServletRequest(HttpServletRequest request) {
            super(request);
            this.response = response;
        }
    
        @Override
        public HttpSession getSession() {
            //return super.getSession(); // 默认使用的是servlet容器session管理
    
            return this.getSession(true);
        }
    
        @Override
        public HttpSession getSession(boolean create) {
            Cookie[] cookies = this.getCookies();
            String sessionId = "";
    
            //这里编写自己获取session的逻辑
            //既然是自定义逻辑,可以从内存中取,也可以从缓存中取。
        }
    }

      也许大家都用过shiro的session管理或者spring-session,其实想要自己去实现,也是很简单的。

    三、缓存InputStream

      自定义工具类 ContentCachingRequestWrapper 

    import java.io.BufferedReader;
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    
    import javax.servlet.ReadListener;
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    
    import org.apache.commons.io.IOUtils;
    
    public class ContentCachingRequestWrapper extends HttpServletRequestWrapper{
        
        private byte[] body;
        
        private BufferedReader reader;
    
        private ServletInputStream inputStream;
    
        public ContentCachingRequestWrapper(HttpServletRequest request) throws IOException{
            super(request);
            body = IOUtils.toByteArray(request.getInputStream());
            inputStream = new RequestCachingInputStream(body);
        }
        
        public byte[] getBody() {
            return body;
        }
        
        @Override
        public ServletInputStream getInputStream() throws IOException {
            if (inputStream != null) {          
                return inputStream;
            }
            return super.getInputStream();
        }
    
        @Override
        public BufferedReader getReader() throws IOException {
            if (reader == null) {
                reader = new BufferedReader(new InputStreamReader(inputStream, getCharacterEncoding()));
            }
            return reader;
        }
        
        private static class RequestCachingInputStream extends ServletInputStream {
            
            private final ByteArrayInputStream inputStream;
    
            public RequestCachingInputStream(byte[] bytes) {
                inputStream = new ByteArrayInputStream(bytes);
            }
            @Override
            public int read() throws IOException {
                return inputStream.read();
            }
    
            @Override
            public boolean isFinished() {
                return inputStream.available() == 0;
            }
    
            @Override
            public boolean isReady() {
                return true;
            }
    
            @Override
            public void setReadListener(ReadListener readlistener) {
            }
    
        }
        
    }

      spring工具类 ContentCachingRequestWrapper 

    package org.springframework.web.util;
    
    import java.io.BufferedReader;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.URLEncoder;
    import java.util.Arrays;
    import java.util.Enumeration;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import org.springframework.http.HttpMethod;
    
    public class ContentCachingRequestWrapper extends HttpServletRequestWrapper {
    
        private static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";
    
    
        private final ByteArrayOutputStream cachedContent;
    
        private ServletInputStream inputStream;
    
        private BufferedReader reader;
    
    
        /**
         * Create a new ContentCachingRequestWrapper for the given servlet request.
         * @param request the original servlet request
         */
        public ContentCachingRequestWrapper(HttpServletRequest request) {
            super(request);
            int contentLength = request.getContentLength();
            this.cachedContent = new ByteArrayOutputStream(contentLength >= 0 ? contentLength : 1024);
        }
    
    
        @Override
        public ServletInputStream getInputStream() throws IOException {
            if (this.inputStream == null) {
                this.inputStream = new ContentCachingInputStream(getRequest().getInputStream());
            }
            return this.inputStream;
        }
    
        @Override
        public String getCharacterEncoding() {
            String enc = super.getCharacterEncoding();
            return (enc != null ? enc : WebUtils.DEFAULT_CHARACTER_ENCODING);
        }
    
        @Override
        public BufferedReader getReader() throws IOException {
            if (this.reader == null) {
                this.reader = new BufferedReader(new InputStreamReader(getInputStream(), getCharacterEncoding()));
            }
            return this.reader;
        }
    
        @Override
        public String getParameter(String name) {
            if (this.cachedContent.size() == 0 && isFormPost()) {
                writeRequestParametersToCachedContent();
            }
            return super.getParameter(name);
        }
    
        @Override
        public Map<String, String[]> getParameterMap() {
            if (this.cachedContent.size() == 0 && isFormPost()) {
                writeRequestParametersToCachedContent();
            }
            return super.getParameterMap();
        }
    
        @Override
        public Enumeration<String> getParameterNames() {
            if (this.cachedContent.size() == 0 && isFormPost()) {
                writeRequestParametersToCachedContent();
            }
            return super.getParameterNames();
        }
    
        @Override
        public String[] getParameterValues(String name) {
            if (this.cachedContent.size() == 0 && isFormPost()) {
                writeRequestParametersToCachedContent();
            }
            return super.getParameterValues(name);
        }
    
    
        private boolean isFormPost() {
            String contentType = getContentType();
            return (contentType != null && contentType.contains(FORM_CONTENT_TYPE) &&
                    HttpMethod.POST.matches(getMethod()));
        }
    
        private void writeRequestParametersToCachedContent() {
            try {
                if (this.cachedContent.size() == 0) {
                    String requestEncoding = getCharacterEncoding();
                    Map<String, String[]> form = super.getParameterMap();
                    for (Iterator<String> nameIterator = form.keySet().iterator(); nameIterator.hasNext(); ) {
                        String name = nameIterator.next();
                        List<String> values = Arrays.asList(form.get(name));
                        for (Iterator<String> valueIterator = values.iterator(); valueIterator.hasNext(); ) {
                            String value = valueIterator.next();
                            this.cachedContent.write(URLEncoder.encode(name, requestEncoding).getBytes());
                            if (value != null) {
                                this.cachedContent.write('=');
                                this.cachedContent.write(URLEncoder.encode(value, requestEncoding).getBytes());
                                if (valueIterator.hasNext()) {
                                    this.cachedContent.write('&');
                                }
                            }
                        }
                        if (nameIterator.hasNext()) {
                            this.cachedContent.write('&');
                        }
                    }
                }
            }
            catch (IOException ex) {
                throw new IllegalStateException("Failed to write request parameters to cached content", ex);
            }
        }
    
        /**
         * Return the cached request content as a byte array.
         */
        public byte[] getContentAsByteArray() {
            return this.cachedContent.toByteArray();
        }
    
    
        private class ContentCachingInputStream extends ServletInputStream {
    
            private final ServletInputStream is;
    
            public ContentCachingInputStream(ServletInputStream is) {
                this.is = is;
            }
    
            @Override
            public int read() throws IOException {
                int ch = this.is.read();
                if (ch != -1) {
                    cachedContent.write(ch);
                }
                return ch;
            }
    
    
            @Override
            public boolean isFinished() {
                return is.available() == 0;
            }
    
            @Override
            public boolean isReady() {
                return true;
            }
    
            @Override
            public void setReadListener(ReadListener readlistener) {
            }
    
        }
    
    }

      获取InputStream

      1、使用自定义工具类的时候调用方法 getBody

      2、使用spring工具类的时候调用方法 getContentAsByteArray

      打印request中的所有请求信息,详细代码如下。

    private void printRequest(HttpServletRequest request) {
        String body = StringUtils.EMPTY;
        try {
            if (request instanceof ContentCachingRequestWrapper) {
                body = new String(((ContentCachingRequestWrapper) request).getContentAsByteArray(), "UTF-8");
                LOGGER.info("Request-Inputstream: " + body);
            }
        } catch (IOException e) {
            LOGGER.error("printRequest 获取body异常...", e);
        }
    
        JSONObject requestJ = new JSONObject();
        JSONObject headers = new JSONObject();
        Collections.list(request.getHeaderNames())
                .stream()
                .forEach(name -> headers.put(name, request.getHeader(name)));
        requestJ.put("headers", headers);
        requestJ.put("parameters", request.getParameterMap());
        requestJ.put("body", body);
        requestJ.put("remote-user", request.getRemoteUser());
        requestJ.put("remote-addr", request.getRemoteAddr());
        requestJ.put("remote-host", request.getRemoteHost());
        requestJ.put("remote-port", request.getRemotePort());
        requestJ.put("uri", request.getRequestURI());
        requestJ.put("url", request.getRequestURL());
        requestJ.put("servlet-path", request.getServletPath());
        requestJ.put("method", request.getMethod());
        requestJ.put("query", request.getQueryString());
        requestJ.put("path-info", request.getPathInfo());
        requestJ.put("context-path", request.getContextPath());
    
        LOGGER.info("Request-Info: " + JSON.toJSONString(requestJ, SerializerFeature.PrettyFormat));
    }

      request中的所有请求信息示例

     Request-Inputstream: {
            "timestamp":1539155028668,
            "appId":"cmos10086e36ipz2otyy8gfqh",
            "nonce":691879,
            "telephone":"18736085778",
            "signature":"226e734a49d513b3b1e364a06fc6f4eb5e2c425c6446ce6a7a950f1d8d6af06c"
    }
    
    Request-Info: {
            "headers":{
                    "x-real-ip":"211.138.20.171",
                    "content-length":"183",
                    "content-encoding":"UTF-8",
                    "host":"221.176.66.251",
                    "connection":"close",
                    "content-type":"application/json",
                    "accept-encoding":"gzip,deflate",
                    "user-agent":"Apache-HttpClient/4.5.3 (Java/1.7.0_76)"
            },
            "remote-host":"172.17.20.92",
            "method":"POST",
            "body":"{"timestamp":1539155028668,"appId":"cmos10086e36ipz2otyy8gfqh","nonce":691879,"telephone":"18736085778","signature":"226e734a49d513b3b1e364a06fc6f4eb5e2c425c6446ce6a7a950f1d8d6af06c"}",
            "uri":"/wmhopenapi/hevb-api/total",
            "url":"http://221.176.66.251/wmhopenapi/hevb-api/total",
            "servlet-path":"/hevb-api/total",
            "remote-addr":"172.17.20.92",
            "context-path":"/wmhopenapi",
            "remote-port":49174,
            "parameters":{}
    }

    四、在Filter中替换掉默认的Request

    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.util.ContentCachingRequestWrapper;
    
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    
    @Configuration
    public class FilterConfig {
    
        @Bean
        public FilterRegistrationBean wmhFilterRegistration() {
            FilterRegistrationBean registration = new FilterRegistrationBean();
            registration.setFilter(new WmhFilter());
            registration.addUrlPatterns("/*");
            registration.setName("MyFilter");
            registration.setOrder(1);
            return registration;
        }
    
        private static class WmhFilter implements Filter {
    
            @Override
            public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
    
            }
    
            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
                chain.doFilter(new ContentCachingRequestWrapper((HttpServletRequest) request), response);
            }
    
            @Override
            public void destroy() {
    
            }
        }
    }

     五、使用场景

      个人认为,在做权限管理、用户管理、登录等场景,尤其是多系统的情况下,常常需要借助第三方的工具比如shiro,spring-session,完成权限、角色、用户、登录的管理逻辑。之前我自己也尝试过使用spring-session+redis缓存实现共享session和单点登录的逻辑。如果时间充分的话,完全可以自己去写一套session管理的工具,并应用到项目中去。

      最近在配合其他组的同时联调接口的时候,遇到了这样的一种情况:他说request body的内容是按照我的协议来的,我后端的实现是通过@RequestBody注解拿到的一个java 对象,有一个字段值为null,很是诡异。于是我俩就纠结是谁的问题,我说他参数传的不对,他说我字段定义不对并让我打印一下Request InputStream,于是就开始寻找解决方案。我们都知道Input Sream只能获取一次,再次获取一定会抛出异常。通过寻找HttpServletRequest的子类,发现了spring提供了这样一个缓存Input Stream内容的工具类,问题迎刃而解。

  • 相关阅读:
    Bootstrap 4/3 页面基础模板 与 兼容旧版本浏览器
    Asp.net core 项目实战 新闻网站+后台 源码、设计原理 、视频教程
    C# 数据类型转换 显式转型、隐式转型、强制转型
    C# 多维数组 交错数组的区别,即 [ , ] 与 [ ][ ]的区别
    C#/Entity Frame Core 使用Linq 进行分页 .Skip() .Take() 的使用方法
    利用 Xunsearch 搭建搜索引擎、内容搜索实战
    Delphi
    Python
    Python
    Python
  • 原文地址:https://www.cnblogs.com/hujunzheng/p/9766739.html
Copyright © 2011-2022 走看看