最近遇到一個(gè)需要替換請(qǐng)求頭內(nèi)容的功能。
考慮到業(yè)務(wù)代碼中使用到請(qǐng)求頭的業(yè)務(wù)代碼量十分巨大。此外這樣子調(diào)整的話代碼的侵入性很高,所以,最終目的還是想著從請(qǐng)求的內(nèi)容進(jìn)行調(diào)整。
最后決定使用過濾器。那么為什么使用過濾器,因?yàn)橄M鹊秸?qǐng)求真正被開始處理的時(shí)候,使用的都是被替換過的請(qǐng)求內(nèi)容。
springmvc中對(duì)于請(qǐng)求的處理流程如下
request->filter->進(jìn)入DispatcherServlet->通過urlhandlermapping找到處理器鏈(攔截器+本身的業(yè)務(wù)方法)
->轉(zhuǎn)成適配器->通過適配器處理->攔截器pre->處理器---業(yè)務(wù)方法->生成視圖->攔截器post->攔截器afterCompletion
->發(fā)送事件->將response返回
為什么選擇過濾器
過濾器與攔截器本身有一個(gè)很大的區(qū)別,過濾器執(zhí)行的過程中并沒有進(jìn)入DispatcherServlet.
如果一個(gè)請(qǐng)求進(jìn)入DispatcherServlet被處理,那么我會(huì)認(rèn)為這個(gè)請(qǐng)求才真正開始被處理。
所以選擇過濾器,是考慮在真的被處理器之前,去完成對(duì)應(yīng)的替換。
那么如何實(shí)現(xiàn)?
首先來看看相關(guān)的類結(jié)構(gòu)

其實(shí)比較簡單。ServletRequest就是請(qǐng)求對(duì)應(yīng)的接口。ServletRequestWrapper則是起了一個(gè)包裝的作用。
至于有什么用?說白了就是提供拓展。
那么直接看看相關(guān)類的源碼
ServletRequest
public interface ServletRequest {
public Object getAttribute(String name);
public Enumeration<String> getAttributeNames();
public String getCharacterEncoding();
public void setCharacterEncoding(String env) throws UnsupportedEncodingException;
public int getContentLength();
public long getContentLengthLong();
public String getContentType();
public ServletInputStream getInputStream() throws IOException;
public String getParameter(String name);
public Enumeration<String> getParameterNames();
public String[] getParameterValues(String name);
public Map<String, String[]> getParameterMap();
public String getProtocol();
public String getScheme();
public String getServerName();
public int getServerPort();
public BufferedReader getReader() throws IOException;
public String getRemoteAddr();
public String getRemoteHost();
public void setAttribute(String name, Object o);
public void removeAttribute(String name);
public Locale getLocale();
public Enumeration<Locale> getLocales();
public boolean isSecure();
public RequestDispatcher getRequestDispatcher(String path);
public String getRealPath(String path);
public int getRemotePort();
public String getLocalName();
public String getLocalAddr();
public int getLocalPort();
public ServletContext getServletContext();
public AsyncContext startAsync() throws IllegalStateException;
public AsyncContext startAsync(ServletRequest servletRequest,
ServletResponse servletResponse)
throws IllegalStateException;
public boolean isAsyncStarted();
public boolean isAsyncSupported();
public AsyncContext getAsyncContext();
public DispatcherType getDispatcherType();
}
方法很多,都是獲取請(qǐng)求相關(guān)內(nèi)容的接口。
ServletRequestWrapper
public class ServletRequestWrapper implements ServletRequest {
private ServletRequest request;
public ServletRequestWrapper(ServletRequest request) {
if (request == null) {
throw new IllegalArgumentException("Request cannot be null");
}
this.request = request;
}
public String getContentType() {
return this.request.getContentType();
}
。。。。若干方法
}
這個(gè)類的作用其實(shí)就是將ServletRequest封裝成成員變量。
各個(gè)方法的實(shí)現(xiàn)都是利用ServletRequest本身的方法去完成的。
HttpServletRequestWrapper
HttpServletRequestWrapper與ServletRequestWrapper也是一樣的,只不過包裝的是HttpServletRequest。
源碼如下
public class HttpServletRequestWrapper extends ServletRequestWrapper implements HttpServletRequest {
public HttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String getHeader(String name) {
return this._getHttpServletRequest().getHeader(name);
}
private HttpServletRequest _getHttpServletRequest() {
return (HttpServletRequest) super.getRequest();
}
}
利用HttpServletRequestWrapper完成拓展。
源碼如下
private class ModifyParametersWrapper extends HttpServletRequestWrapper {
private final Map<String, String> customHeaders;
ModifyParametersWrapper(HttpServletRequest request) {
super(request);
this.customHeaders = new HashMap<>();
}
void putHeader(String name, String value) {
this.customHeaders.put(name, value);
}
@Override
public String getHeader(String name) {
String headerValue = customHeaders.get(name);
if (headerValue != null) {
return headerValue;
}
return ((HttpServletRequest) getRequest()).getHeader(name);
}
@Override
public Enumeration<String> getHeaders(String name) {
String headerValue = customHeaders.get(name);
Set<String> set = new HashSet<String>();
if (headerValue != null) {
set.add(headerValue);
return Collections.enumeration(set);
}
return ((HttpServletRequest) getRequest()).getHeaders(name);
}
@Override
public Enumeration<String> getHeaderNames() {
Set<String> set = new HashSet<>(customHeaders.keySet());
Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
while (e.hasMoreElements()) {
String n = e.nextElement();
set.add(n);
}
return Collections.enumeration(set);
}
}
這個(gè)類的實(shí)現(xiàn)也比較簡單,其實(shí)說白了就是用一個(gè)新的成員變量來存儲(chǔ)請(qǐng)求頭。
優(yōu)先獲取新的請(qǐng)求頭,其次獲取舊的請(qǐng)求頭。
結(jié)合過濾器使用
@WebFilter("/test/*")
public class ProjectConvertFilter implements Filter{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest hsr = (HttpServletRequest)request;
ModifyParametersWrapper mpw = new ModifyParametersWrapper(hsr);
Integer oldprojectId = Integer.valueOf(hsr.getHeader("projectID"));
Integer newProjectId = oldprojectId + 1;
mpw.putHeader("projectID", newProjectId);
chain.doFilter(mpw, response);
}
}
如何無侵入使用
結(jié)合自動(dòng)裝配即可。
@Configuration
@ServletComponentScan(basePackages = "xxx")
public class WebFilterAutoConfiguration {
}