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(); 
        }*/  
    }  
  • 相关阅读:
    CodeForces 156B Suspects(枚举)
    CodeForces 156A Message(暴力)
    CodeForces 157B Trace
    CodeForces 157A Game Outcome
    HDU 3578 Greedy Tino(双塔DP)
    POJ 2609 Ferry Loading(双塔DP)
    Java 第十一届 蓝桥杯 省模拟赛 19000互质的个数
    Java 第十一届 蓝桥杯 省模拟赛 19000互质的个数
    Java 第十一届 蓝桥杯 省模拟赛 19000互质的个数
    Java 第十一届 蓝桥杯 省模拟赛十六进制转换成十进制
  • 原文地址:https://www.cnblogs.com/lizhonghua34/p/7412495.html
Copyright © 2011-2022 走看看