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内容的工具类,问题迎刃而解。

  • 相关阅读:
    UVA 1386 Cellular Automaton
    ZOJ 3331 Process the Tasks
    CodeForces 650B Image Preview
    CodeForces 650A Watchmen
    CodeForces 651B Beautiful Paintings
    CodeForces 651A Joysticks
    HUST 1601 Shepherd
    HUST 1602 Substring
    HUST 1600 Lucky Numbers
    POJ 3991 Seinfeld
  • 原文地址:https://www.cnblogs.com/hujunzheng/p/9766739.html
Copyright © 2011-2022 走看看