首先我們來描述一下在開發中遇到的問題,場景如下:
比如我們要攔截所有請求,獲取請求中的某個參數,進行相應的邏輯處理:比如我要獲取所有請求中的公共參數 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、獲取請求對象中的數據流,通過解析流信息來獲取提交的內容;代碼示例如下:
/**
* 獲取請求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
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();
}*/
}
————————————————
版權聲明:本文爲CSDN博主「代碼老中醫」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/PYXLY1314/article/details/51802652