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

时间:2017-05-25 13:54:40

标签: java java-ee

使用HttpServletRequestWrapper包装HttpServletRequest的目的是什么?这样做我们可以获得什么好处?

1 个答案:

答案 0 :(得分:12)

HttpServletRequest是HTTP特定servlet请求的接口。通常,您可以在servlet filtersservlets中获取此界面的实例。

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

示例:

您有一堆servlet和JSP,它们需要某种格式的请求参数。例如。日期格式为yyyy-MM-dd

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

作为替代方案,您可以实现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) {
  // ???
}

我们需要一个接口HttpServletRequest的新实例,其行为与原始实例req完全相同。但是,getParameter()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多种方法可供实施。其中大多数只是原始请求的包装实现。我们只需要四个自定义实现。但我们必须写下所有这些方法。

所以考虑到课程HttpServletRequestWrapper。这是一个默认的包装器实现,它接受原始请求实例并实现接口HttpServletRequest的所有方法,作为调用原始请求的相应方法的简单包装方法,就像我们上面所做的那样。

通过继承HttpServletRequestWrapper,我们只需要用自定义行为覆盖四个param方法。

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