借助 HttpServletRequestWrapper 修改 Request 内容 --> 装饰者模式的应用

900 阅读4分钟

参考文章 : xiaoqingge.vip/2020/12/05/…

应用场景

Servlet编程模型中经常遇到类似场景,需要修改HttpServletRequest的内容,比如:添加或修改header参数列表

ServletRequest在设计之初是遵循“开闭原则”(类对扩展开放,对修改关闭)的。所以HttpServletRequest默认是只读的,提供了一些获取其属性的方法,未暴露相关setter method,此时便可借助HttpServletRequestWrapper修改其中的内容。

request提供的相关获取属性的方法:

设计原理

HttpServletRequestWrapper继承结构图如下:

其中,HttpServletRequestWrapper继承于ServletRequestWrapper,实现于HttpServletRequest

public class HttpServletRequestWrapper extends ServletRequestWrapper implements HttpServletRequest {
  ...
  }

HttpServletRequestWrapper使用装饰者模式来实现对ServletRequest的包装,

  1. 对现有对象的行为进行覆写(重写ServletRequest相关getter方法);
  2. 添加新的行为职责(新增setter相关方法)。

实际上就是对java语言多态特性的一种应用,关于装饰者模式,有疑问请看我的另一篇文章:Java设计模式–装饰者模式

看一下装饰器如何进行包装的 ServletRequestWrapper 实现 ServletRequest 并包装 ServletRequest

public class ServletRequestWrapper implements ServletRequest {
    private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
    private static final ResourceBundle lStrings =
        ResourceBundle.getBundle(LSTRING_FILE);

    private ServletRequest request;

    /**
     * Creates a ServletRequest adaptor wrapping the given request object.
     *
     * @param request The request to wrap
     *
     * @throws IllegalArgumentException if the request is null
     */
    public ServletRequestWrapper(ServletRequest request) {
        if (request == null) {
            throw new IllegalArgumentException(lStrings.getString("wrapper.nullRequest"));
        }
        this.request = request;
    }

HttpServletRequestWrapper 继承 ServletRequestWrapper, 并包装 HttpServletRequest

public class HttpServletRequestWrapper extends ServletRequestWrapper implements
        HttpServletRequest {

    /**
     * Constructs a request object wrapping the given request.
     *
     * @param request The request to wrap
     *
     * @throws java.lang.IllegalArgumentException
     *             if the request is null
     */
    public HttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }

    private HttpServletRequest _getHttpServletRequest() {
        return (HttpServletRequest) super.getRequest();
    }

HttpServletRequest 是没有暴露出任何setter方法修改的接口的,那我们如何能够传递修改HttpServletRequest后的信息呢?
可以换一个思路,即然HttpServletRequest修改不了,那我们就换个类的对象,这个类对象需要包装HttpServletRequest这个对象,就相当于上面的 HttpServletRequestWrapper 一样,有一个成员变量就是 HttpServletRequest,并且HttpServletRequestWrapper包装类去继承HttpServletRequest「同样也实现了ServletRequest」,拥有HttpServletRequest定义的所有接口。如HttpServletRequest中有getHeader方法,HttpServletRequest里的header信息是没办法修改的,但是我们现在有包装器了呀,包装期并且实现了HttpServletRequest,也会有getHeader,并且我们可以按照自己的逻辑去重写getHeader,我们想加什么就加什么,而且基于java的多态的性质, HttpServletRequest 和 HttpServletRequestWrapper 都是 ServletRequest类型的,所以我们如果想修改原来的HttpServletRequest,那我们就可以选择不再使用原来的HttpServletRequest对象,而是选择使用HttpServletRequestWrapper,HttpServletRequestWrapper 包装 HttpServletRequest,在调用getHeader的时候也不再使用HttpServletRequest的getHeader方法,而是使用HttpServletRequestWrapper的getHeader方法。

具体实现

自定义ServletRequestWrapper

  • 借助HashMap存储请求头与参数列表;
  • 重写getter方法;
  • 新增setter方法(此处命名为addXXX())。
1.  /**
1.  * 自定义 HttpServletRequestWrapper 添加或修改请求头、参数列表
1.  */
1.  public class MapRequestWrapper extends HttpServletRequestWrapper {
1.
1.  /**
1.  * 请求头集合
1.  */
1.  private Map<String, String> headerMap = new HashMap<>();
1.
1.  /**
1.  * 参数集合
1.  */
1.  private Map<String, String[]> paramMap = new HashMap<>();
1.
1.  public MapRequestWrapper(HttpServletRequest request) {
1.  super(request);
1.  // 将 request 中的参数赋值给当前 map
1.  paramMap.putAll(request.getParameterMap());
1.  }
1.
1.  public void addHeader(String name, String value) {
1.  headerMap.put(name, value);
1.  }
1.  // 重写 getHeader 方法,这样在使用包装器对象调用的时候,会调研重写后的方法。
    //先调用被包装对象的getHeader,然后调用重写后的方法进行覆盖
1.  @Override
1.  public String getHeader(String name) {
1.  String value = super.getHeader(name);
1.  if (headerMap.containsKey(name)) {
1.  return headerMap.get(name);
1.  }
1.
1.  return value;
1.  }
1.
1.  @Override
1.  public Enumeration<String> getHeaderNames() {
1.  ArrayList<String> nameList = Collections.list(super.getHeaderNames());
1.  for (String name : headerMap.keySet()) {
1.  nameList.add(name);
1.  }
1.
1.  return Collections.enumeration(nameList);
1.  }
1.
1.  @Override
1.  public Enumeration<String> getHeaders(String name) {
1.  List<String> valueList = Collections.list(super.getHeaders(name));
1.  if (headerMap.containsKey(name)) {
1.  valueList = Arrays.asList(headerMap.get(name));
1.  }
1.
1.  return Collections.enumeration(valueList);
1.  }
1.
1.  @Override
1.  public int getIntHeader(String name) {
1.  return Integer.valueOf(getHeader(name));
1.  }
1.
1.  @Override
1.  public long getDateHeader(String name) {
1.  String value = getHeader(name);
1.  if (value == null) {
1.  return -1L;
1.  } else {
1.  long date = FastHttpDateFormat.parseDate(value);
1.  if (date == -1L) {
1.  throw new IllegalArgumentException(value);
1.  } else {
1.  return date;
1.  }
1.  }
1.  }
1.
1.  public void addParameter(String name, String value) {
1.  String[] valueArr = paramMap.get(name);
1.  if (valueArr == null) {
1.  valueArr = new String[]{name};
1.  paramMap.put(name, valueArr);
1.  } else {
1.  // 将添加的参数作为数组的首元素,并拷贝后续元素
1.  String[] newValueArr = new String[valueArr.length + 1];
1.  newValueArr[0] = value;
1.  for (int i = 0; i < valueArr.length; i++) {
1.  newValueArr[i+1] = valueArr[i];
1.  }
1.  paramMap.put(name, newValueArr);
1.  }
1.  }
1.
1.  public void addParameter(String name, String[] valueArr) {
1.  paramMap.put(name, valueArr);
1.  }
1.
1.  @Override
1.  public Map<String, String[]> getParameterMap() {
1.  return paramMap;
1.  }
1.
1.  @Override
1.  public String getParameter(String name) {
1.  String value = super.getParameter(name);
1.  if (paramMap.containsKey(name)) {
1.  String[] valueArr = paramMap.get(name);
1.  if (valueArr != null && valueArr.length != 0) {
1.  return valueArr[0];
1.  }
1.  }
1.
1.  return value;
1.  }
1.
1.  @Override
1.  public Enumeration<String> getParameterNames() {
1.  ArrayList<String> paramList = Collections.list(super.getParameterNames());
1.  for (String name : paramMap.keySet()) {
1.  paramList.add(name);
1.  }
1.
1.  return Collections.enumeration(paramList);
1.  }
1.
1.  @Override
1.  public String[] getParameterValues(String name) {
1.  String[] valueArr = super.getParameterValues(name);
1.  if (paramMap.containsKey(name)) {
1.  return paramMap.get(name);
1.  }
1.
1.  return valueArr;
1.  }
1.  }

Filter中修改Request

  • 借助HttpServletRequest构建HttpServletRequestWrapper
  • 调用setter方法修改属性。
1.  @Component
1.  @WebFilter
1.  public class TestFilter implements Filter {
1.
1.  @Override
1.  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
1.
1.  HttpServletRequest request = (HttpServletRequest) servletRequest;
    // 使用包装器对象, 包装器重写 getHeader 方法
1.  MapRequestWrapper requestWrapper = new MapRequestWrapper(request);
1.  // 添加请求头
1.  requestWrapper.addHeader("from", "wrapper");
1.
1.  // 添加参数
1.  requestWrapper.addParameter("param2", "wrapper");
1.
1.  filterChain.doFilter(requestWrapper, servletResponse);
1.  }
1.  }

获取修改的属性

  • Controller中获取修改的内容并加以验证。
  1. @Slf4j
  2. @RestController
  3. @RequestMapping("/test")
  4. public class TestController {
  5. @PostMapping("/testRequestWrapper")
  6. public String testRequestWrapper(HttpServletRequest request) {
  7. // 获取本来就有的请求头
  8. String name = request.getHeader("name");
  9. log.info("header name: {}", name);
  10. // 获取添加的请求头
  11. Enumeration from = request.getHeaders("from");
  12. ArrayList fromList = Collections.list(from);
  13. log.info("header from: {}", fromList.toString());
  14. // 获取本来就传的参数
  15. String param1 = request.getParameter("param1");
  16. log.info("parameter param1: {}", param1);
  17. // 获取添加的参数
  18. String[] param2Arr = request.getParameterValues("param2");
  19. log.info("parameter param2: {}", Arrays.asList(param2Arr).toString());
  20. return "SUCCESS";
  21. }
  22. }

结果展示

发送请求:

结果展示:

由图可知,正确获取添加的头信息“from”、“param2”。

功能扩展

可根据实际应用场景,重写或添加新的操作行为,比如修改Cookies、获取输入流等等。