優(yōu)雅地替換請(qǐng)求頭

最近遇到一個(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)


相關(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 {

}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容