關于Filter中獲取請求體body后再次讀取的問題
Filter獲取請求體body再次讀取
工作需要,要將請求和響應做一些處理,寫一個filter攔截請求,攔截request中body內容后,字符流關閉,controller取到的請求體內容為空。
從Request中獲取輸入流,InputStream只能被讀取一次。
解決方案
給request添加一個包裝類BodyWrapper,繼承HttpServletRequestWrapper,
先從request中取輸入流,讀取流中的數據,然后重寫getInputStream()和getReader()方法。
chain.doFilter(requestWrapper, response);
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.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import com.xera.fsafesso.HttpHelper; public class BodyWrapper extends HttpServletRequestWrapper {undefined ? ? private final byte[] body; ? ? public BodyWrapper(HttpServletRequest request) throws IOException {undefined ? ? ? ? super(request); ? ? ? ? body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8")); ? ? } ? ? @Override ? ? public BufferedReader getReader() throws IOException {undefined ? ? ? ? return new BufferedReader(new InputStreamReader(getInputStream())); ? ? } ? ? @Override ? ? public ServletInputStream getInputStream() throws IOException {undefined ? ? ? ? final ByteArrayInputStream bais = new ByteArrayInputStream(body); ? ? ? ? return new ServletInputStream(){undefined ? ? ? ? ? ? @Override ? ? ? ? ? ? public int read() throws IOException {undefined ? ? ? ? ? ? ? ? return bais.read(); ? ? ? ? ? ? } ? ? ? ? ? ? @Override ? ? ? ? ? ? public boolean isFinished() {undefined ? ? ? ? ? ? ? ? return false; ? ? ? ? ? ? } ? ? ? ? ? ? @Override ? ? ? ? ? ? public boolean isReady() {undefined ? ? ? ? ? ? ? ? return false; ? ? ? ? ? ? } ? ? ? ? ? ? @Override ? ? ? ? ? ? public void setReadListener(ReadListener arg0) {undefined ? ? ? ? ? ? } ? ? ? ? }; ? ? } ? ? @Override ? ? public String getHeader(String name) {undefined ? ? ? ? return super.getHeader(name); ? ? } ? ? @Override ? ? public Enumeration<String> getHeaderNames() {undefined ? ? ? ? return super.getHeaderNames(); ? ? } ? ? @Override ? ? public Enumeration<String> getHeaders(String name) {undefined ? ? ? ? return super.getHeaders(name); ? ? } }
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 {undefined ? ? ?/** ? ? ?* 獲取請求Body ? ? ?* @param request ? ? ?* @return ? ? ?*/ ? ? public static String getBodyString(ServletRequest request) {undefined ? ? ? ? StringBuilder sb = new StringBuilder(); ? ? ? ? InputStream inputStream = null; ? ? ? ? BufferedReader reader = null; ? ? ? ? try {undefined ? ? ? ? ? ? inputStream = request.getInputStream(); ? ? ? ? ? ? reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8"))); ? ? ? ? ? ? String line = ""; ? ? ? ? ? ? while ((line = reader.readLine()) != null) {undefined ? ? ? ? ? ? ? ? sb.append(line); ? ? ? ? ? ? } ? ? ? ? } catch (IOException e) {undefined ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? } finally {undefined ? ? ? ? ? ? if (inputStream != null) {undefined ? ? ? ? ? ? ? ? try {undefined ? ? ? ? ? ? ? ? ? ? inputStream.close(); ? ? ? ? ? ? ? ? } catch (IOException e) {undefined ? ? ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? if (reader != null) {undefined ? ? ? ? ? ? ? ? try {undefined ? ? ? ? ? ? ? ? ? ? reader.close(); ? ? ? ? ? ? ? ? } catch (IOException e) {undefined ? ? ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? return sb.toString(); ? ? } }
Filter中寫法如下:
HttpServletRequest httpServletRequest = (HttpServletRequest) request; ? ? ? ? ? ? Map<String, String> requestMap = this.getTypesafeRequestMap(httpServletRequest); ? ? ? ? ? ? requestWrapper = new BodyWrapper(httpServletRequest); ? ? ? ? ? ? String body = HttpHelper.getBodyString(requestWrapper); ? ? ? ? ? ? log.info("loggingFilter---請求路徑 {},請求參數 {},請求體內容 {}",httpServletRequest.getRequestURL(),requestMap,body); ? ? ? chain.doFilter(requestWrapper, response);
在使用注解的方式(即@WebFilter)聲明過濾器時,
需要再main函數類上添加@ServletComponentScan(basePackages = "此處寫明類地址,格式為包名+類名(如com.*)
Http請求解決body流一旦被讀取了就無法二次讀取情況
相信大家在工作當中,經常會遇到需要處理http請求及響應body的場景,這里最大的問題應該就是body中流以但被讀取就無法二次讀取了。
解決request請求流只能讀取一次的問題
我們編寫一個過濾器,這樣就可以重寫body了
package com.interceptor; import java.io.IOException; import java.util.Arrays; import java.util.List; 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; public class MyFilter implements Filter { private static String privateKey ; public String getPrivateKey() { return privateKey; } public void setPrivateKey(String key) { privateKey = key; } /** * 排除過濾路徑 */ List<String> ignore = Arrays.asList("/xxxx"); /** * 前綴排除 如 /static/goods 排除 */ List<String> ignorePrefix = Arrays.asList( "/css/", "/pop/", "/js/", "/static/", "/images/", "/favicon.ico"); /** * 排除過濾路徑 */ List<String> ignoreSuffix = Arrays.asList("/test"); @Override public void init(FilterConfig filterConfig) throws ServletException { //過濾器初始化 } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { // 防止流讀取一次后就沒有了, 所以需要將流繼續(xù)寫出去 HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; String uri = request.getServletPath(); response.setContentType("application/json;charset=UTF-8"); ServletRequest requestWrapper = null; if(canIgnore(uri)) { requestWrapper = new MyFilterBodyReaderHttpServletRequestWrapper(request); filterChain.doFilter(requestWrapper, response); return; } try { requestWrapper = new MyFilterBodyReaderHttpServletRequestWrapper(request, response, privateKey); } catch (Exception e) { e.printStackTrace(); return; } filterChain.doFilter(requestWrapper, servletResponse); } @Override public void destroy() { //過濾器銷毀 } private boolean canIgnore(String uri) { logger.info("過濾器 request uri : {} ",uri); boolean isExcludedPage = false; for (String page : ignore) { if (uri.equals(page)) { logger.info("請求路徑不需要攔截,忽略該uri : {} ",uri); isExcludedPage = true; break; } } for (String prefix : ignorePrefix) { if (uri.startsWith(prefix)) { logger.info("請求路徑前綴[{}],不攔截該uri : {} ", prefix, uri); isExcludedPage = true; break; } } for (String prefix : ignoreSuffix) { if (uri.endsWith(prefix)) { logger.info("請求路徑后綴[{}],不攔截該uri : {} ", prefix, uri); isExcludedPage = true; break; } } return isExcludedPage; } }
package com.interceptor; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.Map; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import com.alibaba.fastjson.JSON; /** * @Description: TODO 過濾器處理requestbody獲取一次就失效 */ public class MyFilterBodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; /** * TODO 重寫requestbody * @param request * @throws IOException */ public MyFilterBodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request); String sessionStream = getBodyString(request); body = sessionStream.getBytes(Charset.forName("UTF-8")); } /** * TODO 攔截解密,校驗,重寫requestbody */ @SuppressWarnings({ "rawtypes", "unchecked" }) public MyFilterBodyReaderHttpServletRequestWrapper(HttpServletRequest request,HttpServletResponse response, String clientKey, Boolean ignoreCheckSign) throws Exception { super(request); String sessionStream = getBodyString(request); Map paramMap = (Map) JSON.parse(sessionStream); /** *自己項目中與合作方的加解密內容 *如:String data= (String) paramMap.get("data"); * String json=xxxxxutil.decrypt(參數); */ body = json.getBytes(Charset.forName("UTF-8")); } /** * TODO 獲取請求Body * @param request * @return * @throws */ public String getBodyString(final ServletRequest request) { StringBuilder sb = new StringBuilder(); InputStream inputStream = null; BufferedReader reader = null; try { inputStream = cloneInputStream(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(); } /** * TODO 無參獲取請求Body * @return * @throws */ public String getBodyString() { if (body == null) { return null; } String str = new String(body); return str; } /** * TODO 復制輸入流 * @param inputStream * @return * @throws */ public InputStream cloneInputStream(ServletInputStream inputStream) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; try { while ((len = inputStream.read(buffer)) > -1) { byteArrayOutputStream.write(buffer, 0, len); } byteArrayOutputStream.flush(); } catch (IOException e) { e.printStackTrace(); } InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); return byteArrayInputStream; } @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 boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } }; } }
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
SpringBoot+MybatisPlus+代碼生成器整合示例
這篇文章主要介紹了SpringBoot+MybatisPlus+代碼生成器整合示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-03-03Maven在Java8下如何忽略Javadoc的編譯錯誤詳解
這篇文章主要給大家介紹了關于Maven在Java8下如何忽略Javadoc的編譯錯誤的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2018-08-08