HttpServletRequestWrapper使用技巧(自定义session和缓存InputStream)
一、前言
javax.servlet.http.HttpServletRequestWrapper 是一個(gè)開發(fā)者可以繼承的類,我們可以重寫相應(yīng)的方法來實(shí)現(xiàn)session的自定義以及緩存InputStream,在程序中可以多次獲取request body的內(nèi)容。
二、自定義seesion
import javax.servlet.http.*;public class CustomizeHttpServletRequest extends HttpServletRequestWrapper {public CustomizeHttpServletRequest(HttpServletRequest request) {super(request);this.response = response;}@Overridepublic HttpSession getSession() {//return super.getSession(); // 默認(rèn)使用的是servlet容器session管理return this.getSession(true);}@Overridepublic HttpSession getSession(boolean create) {Cookie[] cookies = this.getCookies();String sessionId = "";//這里編寫自己獲取session的邏輯//既然是自定義邏輯,可以從內(nèi)存中取,也可以從緩存中取。 } }也許大家都用過shiro的session管理或者spring-session,其實(shí)想要自己去實(shí)現(xiàn),也是很簡(jiǎn)單的。
三、緩存InputStream
自定義工具類?ContentCachingRequestWrapper?
import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader;import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper;import org.apache.commons.io.IOUtils;public class ContentCachingRequestWrapper extends HttpServletRequestWrapper{private byte[] body;private BufferedReader reader;private ServletInputStream inputStream;public ContentCachingRequestWrapper(HttpServletRequest request) throws IOException{super(request);body = IOUtils.toByteArray(request.getInputStream());inputStream = new RequestCachingInputStream(body);}public byte[] getBody() {return body;}@Overridepublic ServletInputStream getInputStream() throws IOException {if (inputStream != null) { return inputStream;}return super.getInputStream();}@Overridepublic BufferedReader getReader() throws IOException {if (reader == null) {reader = new BufferedReader(new InputStreamReader(inputStream, getCharacterEncoding()));}return reader;}private static class RequestCachingInputStream extends ServletInputStream {private final ByteArrayInputStream inputStream;public RequestCachingInputStream(byte[] bytes) {inputStream = new ByteArrayInputStream(bytes);}@Overridepublic int read() throws IOException {return inputStream.read();}@Overridepublic boolean isFinished() {return inputStream.available() == 0;}@Overridepublic boolean isReady() {return true;}@Overridepublic void setReadListener(ReadListener readlistener) {}}}spring工具類?ContentCachingRequestWrapper?
package org.springframework.web.util;import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.URLEncoder; import java.util.Arrays; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import org.springframework.http.HttpMethod;public class ContentCachingRequestWrapper extends HttpServletRequestWrapper {private static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";private final ByteArrayOutputStream cachedContent;private ServletInputStream inputStream;private BufferedReader reader;/*** Create a new ContentCachingRequestWrapper for the given servlet request.* @param request the original servlet request*/public ContentCachingRequestWrapper(HttpServletRequest request) {super(request);int contentLength = request.getContentLength();this.cachedContent = new ByteArrayOutputStream(contentLength >= 0 ? contentLength : 1024);}@Overridepublic ServletInputStream getInputStream() throws IOException {if (this.inputStream == null) {this.inputStream = new ContentCachingInputStream(getRequest().getInputStream());}return this.inputStream;}@Overridepublic String getCharacterEncoding() {String enc = super.getCharacterEncoding();return (enc != null ? enc : WebUtils.DEFAULT_CHARACTER_ENCODING);}@Overridepublic BufferedReader getReader() throws IOException {if (this.reader == null) {this.reader = new BufferedReader(new InputStreamReader(getInputStream(), getCharacterEncoding()));}return this.reader;}@Overridepublic String getParameter(String name) {if (this.cachedContent.size() == 0 && isFormPost()) {writeRequestParametersToCachedContent();}return super.getParameter(name);}@Overridepublic Map<String, String[]> getParameterMap() {if (this.cachedContent.size() == 0 && isFormPost()) {writeRequestParametersToCachedContent();}return super.getParameterMap();}@Overridepublic Enumeration<String> getParameterNames() {if (this.cachedContent.size() == 0 && isFormPost()) {writeRequestParametersToCachedContent();}return super.getParameterNames();}@Overridepublic String[] getParameterValues(String name) {if (this.cachedContent.size() == 0 && isFormPost()) {writeRequestParametersToCachedContent();}return super.getParameterValues(name);}private boolean isFormPost() {String contentType = getContentType();return (contentType != null && contentType.contains(FORM_CONTENT_TYPE) &&HttpMethod.POST.matches(getMethod()));}private void writeRequestParametersToCachedContent() {try {if (this.cachedContent.size() == 0) {String requestEncoding = getCharacterEncoding();Map<String, String[]> form = super.getParameterMap();for (Iterator<String> nameIterator = form.keySet().iterator(); nameIterator.hasNext(); ) {String name = nameIterator.next();List<String> values = Arrays.asList(form.get(name));for (Iterator<String> valueIterator = values.iterator(); valueIterator.hasNext(); ) {String value = valueIterator.next();this.cachedContent.write(URLEncoder.encode(name, requestEncoding).getBytes());if (value != null) {this.cachedContent.write('=');this.cachedContent.write(URLEncoder.encode(value, requestEncoding).getBytes());if (valueIterator.hasNext()) {this.cachedContent.write('&');}}}if (nameIterator.hasNext()) {this.cachedContent.write('&');}}}}catch (IOException ex) {throw new IllegalStateException("Failed to write request parameters to cached content", ex);}}/*** Return the cached request content as a byte array.*/public byte[] getContentAsByteArray() {return this.cachedContent.toByteArray();}private class ContentCachingInputStream extends ServletInputStream {private final ServletInputStream is;public ContentCachingInputStream(ServletInputStream is) {this.is = is;}@Overridepublic int read() throws IOException {int ch = this.is.read();if (ch != -1) {cachedContent.write(ch);}return ch;}@Overridepublic boolean isFinished() {return is.available() == 0;}@Overridepublic boolean isReady() {return true;}@Overridepublic void setReadListener(ReadListener readlistener) {}}}獲取InputStream
1、使用自定義工具類的時(shí)候調(diào)用方法?getBody
2、使用spring工具類的時(shí)候調(diào)用方法?getContentAsByteArray
打印request中的所有請(qǐng)求信息,詳細(xì)代碼如下。
private void printRequest(HttpServletRequest request) {String body = StringUtils.EMPTY;try {if (request instanceof ContentCachingRequestWrapper) {body = new String(((ContentCachingRequestWrapper) request).getContentAsByteArray(), "UTF-8");LOGGER.info("Request-Inputstream: " + body);}} catch (IOException e) {LOGGER.error("printRequest 獲取body異常...", e);}JSONObject requestJ = new JSONObject();JSONObject headers = new JSONObject();Collections.list(request.getHeaderNames()).stream().forEach(name -> headers.put(name, request.getHeader(name)));requestJ.put("headers", headers);requestJ.put("parameters", request.getParameterMap());requestJ.put("body", body);requestJ.put("remote-user", request.getRemoteUser());requestJ.put("remote-addr", request.getRemoteAddr());requestJ.put("remote-host", request.getRemoteHost());requestJ.put("remote-port", request.getRemotePort());requestJ.put("uri", request.getRequestURI());requestJ.put("url", request.getRequestURL());requestJ.put("servlet-path", request.getServletPath());requestJ.put("method", request.getMethod());requestJ.put("query", request.getQueryString());requestJ.put("path-info", request.getPathInfo());requestJ.put("context-path", request.getContextPath());LOGGER.info("Request-Info: " + JSON.toJSONString(requestJ, SerializerFeature.PrettyFormat)); }request中的所有請(qǐng)求信息示例
Request-Inputstream: {"timestamp":1539155028668,"appId":"cmos10086e36ipz2otyy8gfqh","nonce":691879,"telephone":"18736085778","signature":"226e734a49d513b3b1e364a06fc6f4eb5e2c425c6446ce6a7a950f1d8d6af06c" }Request-Info: {"headers":{"x-real-ip":"211.138.20.171","content-length":"183","content-encoding":"UTF-8","host":"221.176.66.251","connection":"close","content-type":"application/json","accept-encoding":"gzip,deflate","user-agent":"Apache-HttpClient/4.5.3 (Java/1.7.0_76)"},"remote-host":"172.17.20.92","method":"POST","body":"{\"timestamp\":1539155028668,\"appId\":\"cmos10086e36ipz2otyy8gfqh\",\"nonce\":691879,\"telephone\":\"18736085778\",\"signature\":\"226e734a49d513b3b1e364a06fc6f4eb5e2c425c6446ce6a7a950f1d8d6af06c\"}","uri":"/wmhopenapi/hevb-api/total","url":"http://221.176.66.251/wmhopenapi/hevb-api/total","servlet-path":"/hevb-api/total","remote-addr":"172.17.20.92","context-path":"/wmhopenapi","remote-port":49174,"parameters":{} }四、在Filter中替換掉默認(rèn)的Request
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.util.ContentCachingRequestWrapper;import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException;@Configuration public class FilterConfig {@Beanpublic FilterRegistrationBean wmhFilterRegistration() {FilterRegistrationBean registration = new FilterRegistrationBean();registration.setFilter(new WmhFilter());registration.addUrlPatterns("/*");registration.setName("MyFilter");registration.setOrder(1);return registration;}private static class WmhFilter implements Filter {@Overridepublic void init(javax.servlet.FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {chain.doFilter(new ContentCachingRequestWrapper((HttpServletRequest) request), response);}@Overridepublic void destroy() {}} }?五、使用場(chǎng)景
個(gè)人認(rèn)為,在做權(quán)限管理、用戶管理、登錄等場(chǎng)景,尤其是多系統(tǒng)的情況下,常常需要借助第三方的工具比如shiro,spring-session,完成權(quán)限、角色、用戶、登錄的管理邏輯。之前我自己也嘗試過使用spring-session+redis緩存實(shí)現(xiàn)共享session和單點(diǎn)登錄的邏輯。如果時(shí)間充分的話,完全可以自己去寫一套session管理的工具,并應(yīng)用到項(xiàng)目中去。
最近在配合其他組的同時(shí)聯(lián)調(diào)接口的時(shí)候,遇到了這樣的一種情況:他說request body的內(nèi)容是按照我的協(xié)議來的,我后端的實(shí)現(xiàn)是通過@RequestBody注解拿到的一個(gè)java 對(duì)象,有一個(gè)字段值為null,很是詭異。于是我倆就糾結(jié)是誰的問題,我說他參數(shù)傳的不對(duì),他說我字段定義不對(duì)并讓我打印一下Request InputStream,于是就開始尋找解決方案。我們都知道Input Sream只能獲取一次,再次獲取一定會(huì)拋出異常。通過尋找HttpServletRequest的子類,發(fā)現(xiàn)了spring提供了這樣一個(gè)緩存Input Stream內(nèi)容的工具類,問題迎刃而解。
轉(zhuǎn)載于:https://www.cnblogs.com/hujunzheng/p/9766739.html
總結(jié)
以上是生活随笔為你收集整理的HttpServletRequestWrapper使用技巧(自定义session和缓存InputStream)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 十四五时期农业银行两大定位
- 下一篇: 支付宝怎么搜不到来分期了