当前位置: 首页 > 知识库问答 >
问题:

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

严宇
2023-03-14

使用HttpServletRequestWrapper包装HttpServletRequest的目的是什么?我们这样做有什么好处?

共有1个答案

孙泳
2023-03-14

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

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

示例:

您有一堆servlet和JSP,它们期望以某种格式提供一些请求参数。例如。格式为yyyy-mm-dd的日期。

现在还需要以不同的格式支持日期,比如dd.mm.yyyy具有相同的功能。假设没有中心string to date函数(它是继承的遗留应用程序),您必须找到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);
    }
  });
}
 类似资料:
  • 到目前为止还不错! 使用GADT语法,他还定义了数据类型SNAT: 基本上,他只将Nat类型包装到一个SNat构造函数中。为什么要这样做呢?我们有什么收获?数据类型Nat和SNat不同构吗?为什么SNat是单身,为什么Nat不是单身?在这两种情况下,每个类型都有一个值,即相应的自然数。

  • 问题内容: 我从Angular 2.0开始了一个教程,设置工作区的第一步是安装Node.js和NPM。 为什么我们要为Angular 2.0安装Node.js? 我不记得要对有角度的1.X这样做。 问题答案: 从技术上讲 ,不需要Angular2即可使用Node.js和NPM。它确实使事情变得轻松。这是我推测此选择背后的主要原因: CLI :一段时间以来,构建和开发新的Angular应用程序的实际

  • 问题内容: 我们在Java中有包装器类,例如Interger,Float ..为什么它仍然仍然支持原语,这使Java成为完全面向对象的语言? 问题答案: 包装器是对象,被放置在堆中。基元只是“值”,并进入堆栈。这是更有效的,因为对于堆中的已包装基元,您(至少)需要值(位于堆栈中) 和 对包装器对象的引用。 这种性能提升是否重要根本取决于您在做什么。当然,对于繁重的数字工作而言,但对于其中99%的工

  • C++20概念的一个特点是,在某些情况下,您必须编写。例如,[expr.prim.req]/3中的这个示例:

  • 问题内容: 我想知道一个仅在Eclipse上使用Maven或Ant的具体示例。 当我在Eclipse中进行开发时,Eclipse会为我做所有事情,而我只需要单击run按钮。而且,Eclipse可以让您将代码导出到Windows的可运行jar或.exe中。 所以我真的不知道为什么我需要Maven或Ant。 而且,如果确实需要, 我应该选择Maven还是Ant? 问题答案: 因为您的同事可能更喜欢Ne

  • 标记有助于在视图层次结构中将一个布局包含在另一个布局中时消除冗余的视图组。例如,如果您的主布局是一个垂直的,其中两个连续的视图可以在多个布局中重用,那么放置这两个视图的可重用布局需要它自己的根视图。但是,使用另一个作为可重用布局的根将导致在垂直的中出现垂直的。嵌套的除了降低UI性能之外,没有任何实际用途。 罗曼,你在哪?