首先咱們來描述一下在開發中遇到的問題,場景以下:java
好比咱們要攔截全部請求,獲取請求中的某個參數,進行相應的邏輯處理:好比我要獲取全部請求中的公共參數 token,clientVersion等等;這個時候咱們一般有兩種作法json
前提條件是咱們實現Filter類,重寫doFilter方法app
一、經過getParameter方法得到ide
HttpServletRequest hreq = (HttpServletRequest) req;測試
String param = hreq.getParameter("param");ui
這種方法有缺陷:它只能獲取 POST 提交方式中的 this
Content-Type: application/x-www-form-urlencoded;
這種提交方式中,key爲param ,若提交類型不是這種方式就獲取不到param的值url
二、獲取請求對象中的數據流,經過解析流信息來獲取提交的內容;代碼示例以下:spa
/** * 獲取請求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解析成 字符串的形式;而後再對字符串的格式進行業務解析;.net
這種處理方式的優勢是:能解析 出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
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); } }
二、輔助類
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(); }*/ }