zoukankan      html  css  js  c++  java
  • 解决在Filter中读取Request中的流后,后续controller或restful接口中无法获取流的问题


    首先我们来描述一下在开发中遇到的问题,场景如下:

    比如我们要拦截所有请求,获取请求中的某个参数,进行相应的逻辑处理:比如我要获取所有请求中的公共参数 token,clientVersion等等;这个时候我们通常有两种做法

     前提条件是我们实现Filter类,重写doFilter方法

    1、通过getParameter方法获得

          HttpServletRequest hreq = (HttpServletRequest) req;

          String param = hreq.getParameter("param");

    这种方法有缺陷:它只能获取  POST 提交方式中的  

      Content-Type: application/x-www-form-urlencoded;

        这种提交方式中,key为param ,若提交类型不是这种方式就获取不到param的值


    2、获取请求对象中的数据流,通过解析流信息来获取提交的内容;代码示例如下:

    [java] view plain copy
            /** 
             * 获取请求Body 
             * 
             * @param request 
             * @return 
             */  
            public static String getBodyString(ServletRequest request) {  
                StringBuilder sb = new StringBuilder();  
                InputStream inputStream = null;  
                BufferedReader reader = null;  
                try {  
                    inputStream = request.getInputStream();  
                    reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));  
                    String line = "";  
                    while ((line = reader.readLine()) != null) {  
                        sb.append(line);  
                    }  
                } catch (IOException e) {  
                    e.printStackTrace();  
                } finally {  
                    if (inputStream != null) {  
                        try {  
                            inputStream.close();  
                        } catch (IOException e) {  
                            e.printStackTrace();  
                        }  
                    }  
                    if (reader != null) {  
                        try {  
                            reader.close();  
                        } catch (IOException e) {  
                            e.printStackTrace();  
                        }  
                    }  
                }  
                return sb.toString();  
            }  
    


    通过这种简单的解析方式,将http请求中的body解析成 字符串的形式;然后再对字符串的格式进行业务解析;

    这种处理方式的优点是:能解析 出content-Type 为  application/x-www-form-urlencoded ,application/json , text/xml 这三种提交方式的 数据 (至于另外一种带文件的请求:multipart/form-data ,本人没有亲自测试过,等后期遇到此类为题再来解决)。 上述方法返回的可能是key,value(对应第一种content-type),可能是json字符串(对应第二种),可能是xml的字符串(对应第三种)

    但是这种方式有一个很大的缺点:那就是当从请求中获取流以后,流被filter中的这个 inputStreamToString(InputStream in) 这个方法处理后就被“消耗”了,这会导致,chain.doFilter(request, res)这个链在传递 request对象的时候,里面的请求流为空,导致责任链模式下,其他下游的链无法获取请求的body,从而导致程序无法正常运行,这也使得我们的这个filter虽然可以获取请求信息,但是它会导致整个应用程序不可用,那么它也就失去了意义;

        针对第二种方式的缺陷:流一旦被读取,就无法向下传递整个问题,有如下解决方案;

        解决思路如下:将取出来的字符串,再次转换成流,然后把它放入到新request 对象中,在chain.doFiler方法中 传递新的request对象;要实现这种思路,需要自定义一个类

    继承HttpServletRequestWrapper,然后重写public BufferedReader getReader()方法,public  ServletInputStream getInputStream()方法;(这两个方法的重写实现逻辑如下:getInputStream()方法中将body体中的字符串转换为字节流(它实质上返回的是一个ServletInputStream 对象);然后通过getReader()调用---->getInputStream()方法;),继承实现重写逻辑以后,在自定义分filter(VersionCheckFilter)中,使用自定义的HttpServletRequestWrapper(BodyReaderHttpServletRequestWrapper)将原始的HttpServletRequest对象进行再次封装;再通过BodyReaderHttpServletRequestWrapper对象去做dofilter(req,res)的req对象;

       涉及的三个类的代码如下:

       自定义的HttpServletRequestWrapper

    [java] view plain copy
    import java.io.BufferedReader;  
    import java.io.ByteArrayInputStream;  
    import java.io.IOException;  
    import java.io.InputStreamReader;  
    import java.nio.charset.Charset;  
    import java.util.Enumeration;  
      
    import javax.servlet.ServletInputStream;  
    import javax.servlet.http.HttpServletRequest;  
    import javax.servlet.http.HttpServletRequestWrapper;  
      
      
      
    import com.yt.util.HttpHelper;  
      
    public class BodyReaderHttpServletRequestWrapper extends  
            HttpServletRequestWrapper {  
          
        private final byte[] body;  
      
        public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {  
            super(request);  
            System.out.println("-------------------------------------------------");    
            Enumeration e = request.getHeaderNames()   ;    
             while(e.hasMoreElements()){    
                 String name = (String) e.nextElement();    
                 String value = request.getHeader(name);    
                 System.out.println(name+" = "+value);    
                     
             }    
            body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8"));  
        }  
      
        @Override  
        public BufferedReader getReader() throws IOException {  
            return new BufferedReader(new InputStreamReader(getInputStream()));  
        }  
      
        @Override  
        public ServletInputStream getInputStream() throws IOException {  
      
            final ByteArrayInputStream bais = new ByteArrayInputStream(body);  
      
            return new ServletInputStream() {  
                @Override  
                public int read() throws IOException {  
                    return bais.read();  
                }  
            };  
        }  
      
        @Override  
        public String getHeader(String name) {  
            return super.getHeader(name);  
        }  
      
        @Override  
        public Enumeration<String> getHeaderNames() {  
            return super.getHeaderNames();  
        }  
      
        @Override  
        public Enumeration<String> getHeaders(String name) {  
            return super.getHeaders(name);  
        }  
          
    }  

     2、辅助类

    HttpHelper

     
    import java.io.BufferedReader;  
    import java.io.IOException;  
    import java.io.InputStream;  
    import java.io.InputStreamReader;  
    import java.nio.charset.Charset;  
      
    import javax.servlet.ServletRequest;  
      
    public class HttpHelper {  
        /** 
         * 获取请求Body 
         * 
         * @param request 
         * @return 
         */  
        public static String getBodyString(ServletRequest request) {  
            StringBuilder sb = new StringBuilder();  
            InputStream inputStream = null;  
            BufferedReader reader = null;  
            try {  
                inputStream = request.getInputStream();  
                reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));  
                String line = "";  
                while ((line = reader.readLine()) != null) {  
                    sb.append(line);  
                }  
            } catch (IOException e) {  
                e.printStackTrace();  
            } finally {  
                if (inputStream != null) {  
                    try {  
                        inputStream.close();  
                    } catch (IOException e) {  
                        e.printStackTrace();  
                    }  
                }  
                if (reader != null) {  
                    try {  
                        reader.close();  
                    } catch (IOException e) {  
                        e.printStackTrace();  
                    }  
                }  
            }  
            return sb.toString();  
        }  
    } 

     3.自定义的filter

    import java.io.IOException;  
    import java.io.InputStream;  
    import java.io.PrintWriter;  
    import java.net.URL;  
    import java.net.URLDecoder;  
    import java.util.Map;  
      
    import javax.servlet.Filter;  
    import javax.servlet.FilterChain;  
    import javax.servlet.FilterConfig;  
    import javax.servlet.ServletException;  
    import javax.servlet.ServletRequest;  
    import javax.servlet.ServletResponse;  
    import javax.servlet.http.HttpServletRequest;  
    import javax.servlet.http.HttpServletResponse;  
    import javax.xml.crypto.URIDereferencer;  
      
    import com.yt.util.HttpHelper;  
    import com.yt.util.JsonHelper;  
      
    public class VersionCheckFilter implements Filter {  
      
        @Override  
        public void destroy() {  
      
        }  
      
        @Override  
        public void doFilter(ServletRequest req, ServletResponse res,  
                FilterChain chain) throws IOException, ServletException {  
            HttpServletRequest hreq = (HttpServletRequest) req;  
            String uri = hreq.getRequestURI();  
            if(uri.contains("uploadImageServlet")){  
                //图像上传的请求,不做处理  
                chain.doFilter(req, res);  
            }else{  
                String reqMethod = hreq.getMethod();  
                if("POST".equals(reqMethod)){  
                      
                    PrintWriter out = null;   
                    HttpServletResponse response = (HttpServletResponse) res;  
                    response.setCharacterEncoding("UTF-8");    
                    response.setContentType("application/json; charset=utf-8");    
                      
                  /* String requestStr = this.inputStreamToString(hreq.getInputStream()); 
     
                    String[] arrs = requestStr.split("&");  
                    for (String strs : arrs) { 
                        String[] strs2 = strs.split("="); 
                        for (int i = 0; i < strs2.length; i++) { 
                            if (strs2[0].equals("param")) { 
                                if (strs2[1] != null &&!strs2[1].equals("")) { 
                                    System.out.println("test=" + strs2[1]); 
                                } 
                            } 
                        } 
                    }*/  
                      
                   ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(hreq);  
                   String body = HttpHelper.getBodyString(requestWrapper);  
                      
                    //如果是POST请求则需要获取 param 参数  
                   String param = URLDecoder.decode(body,"utf-8");  
                    //String param = hreq.getParameter("param");  
                    //json串 转换为Map  
                    if(param!=null&¶m.contains("=")){  
                        param = param.split("=")[1];  
                    }  
                    Map paramMap = JsonHelper.getGson().fromJson(param, Map.class);  
                    Object obj_clientversion = paramMap.get("clientVersion");  
                      
                    String clientVersion = null;  
                    if(obj_clientversion != null){  
                        clientVersion = obj_clientversion.toString();  
                        System.out.println(clientVersion);  
                        /*try {   
                            out = response.getWriter(); 
                            Map remap = new HashMap<String, Object>(); 
                            remap.put("code", 9); 
                            remap.put("message", Constant.SYSTEM_ERR_DESC); 
                            out.append(JsonHelper.getGson().toJson(remap));   
                        } catch (IOException e) {   
                            e.printStackTrace();   
                        } finally {   
                            if (out != null) {   
                                out.close();   
                            }   
                        }*/  
                        chain.doFilter(requestWrapper, res);      
                }else{  
                    chain.doFilter(requestWrapper, res);      
                }  
                }else{  
                    //get请求直接放行  
                    chain.doFilter(req, res);  
                }  
            }  
        }  
      
        @Override  
        public void init(FilterConfig arg0) throws ServletException {  
              
        }  
      
        /*public String inputStreamToString(InputStream in) throws IOException { 
            StringBuffer out = new StringBuffer(); 
            byte[] b = new byte[4096]; 
            for (int n; (n = in.read(b)) != -1;) { 
                out.append(new String(b, 0, n)); 
            } 
            return out.toString(); 
        }*/  
    }  
  • 相关阅读:
    Win下的批处理命令
    二分查找
    Leetcode504.Base 7七进制数
    Leetcode500.Keyboard Row键盘行
    Leetcode492.Construct the Rectangle构造矩形
    Leetcode485.Max Consecutive Ones最大连续1的个数
    Leetcode475.Heaters供暖器
    hdu1233还是畅通工程
    hdu1863畅通工程
    Leetcode459.Repeated Substring Pattern重复的子字符串
  • 原文地址:https://www.cnblogs.com/lizhonghua34/p/7412495.html
Copyright © 2011-2022 走看看