为什么我们要包装 HttpServletRequest ?该 api 提供了一个 HttpServletRequestWrapper,但是我们从包装请求中获得了什么?

2022-09-02 11:49:07

使用 HttpServletRequestWrapper 包装 HttpServletRequest 的目的是什么?我们从这样做中得到什么好处?


答案 1

HttpServletRequest是HTTP特定servlet请求的接口。通常,您可以在 servlet 过滤器或 servlet 中获取此接口实例。

有时,您希望在某个时候调整原始请求。使用HttpServletRequestWrapper,您可以包装原始请求并覆盖一些方法,使其行为略有不同。

例:

你有一堆 servlet 和 JSP,它们需要一些特定格式的请求参数。例如,格式的日期。yyyy-MM-dd

现在,需要以不同的格式支持日期,就像具有相同的功能一样。假设到目前为止没有中心字符串函数(它是一个继承的旧版应用程序),你必须找到servlet和JSP中的所有位置。dd.MM.yyyy

作为替代方法,您可以实现 servlet 过滤器。您可以映射过滤器,以便对 servlet 和 JSP 的所有请求都将通过此过滤器。

过滤器的目的是检查日期参数的格式,并在必要时将其重新格式化为旧格式。servlet 和 JSP 始终以预期的旧格式获取日期字段。无需更改它们。

这是过滤器的骨架:

public void doFilter(ServletRequest request, ServletResponse response, 
    FilterChain chain) throws IOException, ServletException {
  HttpServletRequest adjustedRequest = adjustParamDates((HttpServletRequest) request);
  chain.doFilter(adjustedRequest, response);
}

我们接受原始请求,并在方法中操作请求并将其向下传递到筛选器链中。adjustParamDates()

现在,我们将如何实现?adjustParamDates()

private HttpServletRequest adjustParamDates(HttpServletRequest req) {
  // ???
}

我们需要一个新的接口实例,它的行为与原始实例完全相同。但是这四种方法, , 不应该在原始参数上工作,而应该在调整后的参数集上工作。所有其他接口方法的行为都应与原始方法类似。HttpServletRequestreqgetParameter()getParameterMap()getParameterNames()getParameterValues()HttpServletRequest

所以我们可以做这样的事情。我们创建所有方法的实例并实现所有方法。通过调用原始请求实例的相应方法,大多数方法实现都非常简单:HttpServletRequest

private HttpServletRequest adjustParamDates(final HttpServletRequest req) {
  final Map<String, String[]> adjustedParams = reformatDates(req.getParameterMap());
  return new HttpServletRequest() {
    public boolean authenticate(HttpServletResponse response) {
      return req.authenticate(response);
    }

    public String changeSessionId() {
      return req.changeSessionId();
    }

    public String getContextPath() {
      return req.getContextPath();
    }

    // Implement >50 other wrapper methods
    // ...

    // Now the methods with different behaviour:
    public String getParameter(String name) {
      return adjustedParams.get(name) == null ? null : adjustedParams.get(name)[0];
    }

    public Map<String, String[]> getParameterMap() {
      return adjustedParams;
    }

    public Enumeration<String> getParameterNames() {
      return Collections.enumeration(adjustedParams.keySet());
    }

    public String[] getParameterValues(String name) {
      return adjustedParams.get(name);
    }
  });
}

有 50 多种方法需要实现。它们中的大多数只是原始请求的包装器实现。我们只需要四个自定义实现。但是我们必须写下所有这些方法。

因此,这里考虑了类。这是一个默认的包装器实现,它采用原始请求实例,并将接口的所有方法实现为简单的包装器方法,调用原始请求的相应方法,就像我们上面所做的那样。HttpServletRequestWrapperHttpServletRequest

通过子类化,我们只需要用自定义行为覆盖四个参数方法。HttpServletRequestWrapper

private HttpServletRequest adjustParamDates(final HttpServletRequest req) {
  final Map<String, String[]> adjustedParams = reformatDates(req.getParameterMap());
  return new HttpServletRequestWrapper(req) {
    public String getParameter(String name) {
      return adjustedParams.get(name) == null ? null : adjustedParams.get(name)[0];
    }

    public Map<String, String[]> getParameterMap() {
      return adjustedParams;
    }

    public Enumeration<String> getParameterNames() {
      return Collections.enumeration(adjustedParams.keySet());
    }

    public String[] getParameterValues(String name) {
      return adjustedParams.get(name);
    }
  });
}

答案 2

推荐