美文网首页
优雅地替换请求头

优雅地替换请求头

作者: guessguess | 来源:发表于2021-12-24 14:07 被阅读0次

最近遇到一个需要替换请求头内容的功能。
考虑到业务代码中使用到请求头的业务代码量十分巨大。此外这样子调整的话代码的侵入性很高,所以,最终目的还是想着从请求的内容进行调整。

最后决定使用过滤器。那么为什么使用过滤器,因为希望等到请求真正被开始处理的时候,使用的都是被替换过的请求内容。

springmvc中对于请求的处理流程如下
request->filter->进入DispatcherServlet->通过urlhandlermapping找到处理器链(拦截器+本身的业务方法)
->转成适配器->通过适配器处理->拦截器pre->处理器---业务方法->生成视图->拦截器post->拦截器afterCompletion
->发送事件->将response返回

为什么选择过滤器

过滤器与拦截器本身有一个很大的区别,过滤器执行的过程中并没有进入DispatcherServlet.
如果一个请求进入DispatcherServlet被处理,那么我会认为这个请求才真正开始被处理。
所以选择过滤器,是考虑在真的被处理器之前,去完成对应的替换。

那么如何实现?

首先来看看相关的类结构


相关结构

其实比较简单。ServletRequest就是请求对应的接口。ServletRequestWrapper则是起了一个包装的作用。
至于有什么用?说白了就是提供拓展。
那么直接看看相关类的源码

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();
}

方法很多,都是获取请求相关内容的接口。

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();
    }
    。。。。若干方法
}

这个类的作用其实就是将ServletRequest封装成成员变量。
各个方法的实现都是利用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);
        }
    }

这个类的实现也比较简单,其实说白了就是用一个新的成员变量来存储请求头。
优先获取新的请求头,其次获取旧的请求头。

结合过滤器使用

@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);
    }
}

如何无侵入使用

结合自动装配即可。

@Configuration
@ServletComponentScan(basePackages = "xxx")
public class WebFilterAutoConfiguration {

}

相关文章

  • 优雅地替换请求头

    最近遇到一个需要替换请求头内容的功能。考虑到业务代码中使用到请求头的业务代码量十分巨大。此外这样子调整的话代码的侵...

  • 3.4.3 头信息操作进阶

    1. 添加请求头 setAllHTTPHeaderFields: 方法提供了一种方式以通过一次调用替换掉所有的请求...

  • Linux vim命令

    vim 命令 在 Vim 中优雅地查找和替换 :s(substitute)命令用来查找和替换字符串。语法如下 ^E...

  • 【Nginx】配置自定义环境变量

    Nginx在处理客户端请求header头时,会将名称中的‘-’替换为‘’,所有字母变为小写,加上前缀“$http”...

  • 如何优雅地请求别人引荐自己?

    在职场,如果能够得到伯乐的引荐,无疑对你的职业生涯成长带来帮助。然而现在,人人都很忙,要得到他人的帮助,你不能坐等...

  • 如何优雅地求助?

    请求客户帮忙介绍客户、请求领导指导、请求同事帮忙……我们在工作和生活中免不了要向别人求助,如何优雅地求助,而不是低...

  • 关于HTTP请求头

    GET请求头 POST请求头

  • 请求头  网络 请求:

    IOS NSURLRequest 设置 Headerhttps://my.oschina.net/wolx/blo...

  • http报文

    起始行:请求起始行、响应起始行首部(头):请求头、响应头、通用头主体:请求主体、响应主体 general 通用头 ...

  • JAVA 代码调用Jmeter (动态配置请求参数)

    一、Jmeter如何进行动态设置请求参数 Jmeter使用占位符的方式进行动态替换请求参数内容。 那么替换占位符的...

网友评论

      本文标题:优雅地替换请求头

      本文链接:https://www.haomeiwen.com/subject/udihqrtx.html