【问题标题】:Difference between / and /* in servlet mapping url patternservlet 映射 url 模式中 / 和 /* 之间的区别
【发布时间】:2020-03-22 20:02:22
【问题描述】:

熟悉的代码:

<servlet-mapping>
    <servlet-name>main</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>main</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

我的理解是/*映射到http://host:port/context/*

/ 怎么样?它肯定不会仅映射到 http://host:port/context 根。事实上,它会接受http://host:port/context/hello,但拒绝http://host:port/context/hello.jsp

谁能解释http://host:port/context/hello 是如何映射的?

【问题讨论】:

    标签: servlets web.xml url-pattern


    【解决方案1】:

    &lt;url-pattern&gt;/*&lt;/url-pattern&gt;

    servlet 上的/* 覆盖所有其他servlet,包括servlet 容器提供的所有servlet,例如默认servlet 和JSP servlet。无论您发出什么请求,它都会在那个 servlet 中结束。因此,这对于 servlet 来说是一个糟糕的 URL 模式。通常,您只想在Filter 上使用/*。通过调用FilterChain#doFilter(),它可以让请求继续到侦听更具体的 URL 模式的任何 servlet。

    &lt;url-pattern&gt;/&lt;/url-pattern&gt;

    / 不会覆盖任何其他 servlet。它只替换 servletcontainer 的内置默认 servlet,用于所有与任何其他已注册 servlet 不匹配的请求。这通常只在静态资源(CSS/JS/image/etc)和目录列表上调用。 servletcontainer 内置的默认 servlet 还能够处理 HTTP 缓存请求、媒体(音频/视频)流和文件下载恢复。通常,您不希望覆盖默认 servlet,否则您将不得不处理它的所有任务,这并非微不足道(JSF 实用程序库 OmniFaces 有一个 open source example)。因此,这对于 servlet 来说也是一个糟糕的 URL 模式。至于为什么 JSP 页面没有命中这个 servlet,是因为 servletcontainer 内置的 JSP servlet 会被调用,默认情况下已经映射到更具体的 URL 模式*.jsp

    &lt;url-pattern&gt;&lt;/url-pattern&gt;

    还有空字符串 URL 模式 。这将在请求上下文根时调用。这与&lt;welcome-file&gt; 方法不同,它在请求任何子文件夹时不会被调用。如果您想要“home page servlet”,这很可能是您实际正在寻找的 URL 模式。我只需要承认,我直观地期望空字符串 URL 模式 和斜杠 URL 模式 / 完全相反地定义,所以我可以理解很多初学者对此感到困惑。但事实就是如此。

    前置控制器

    如果您实际上打算拥有一个前端控制器 servlet,那么您最好将其映射到更具体的 URL 模式上,例如 *.html*.do/pages/*、@987654346 @ 等。借助 servlet 过滤器,您可以隐藏前端控制器 URL 模式并覆盖常见 URL 模式(如 /resources/*/static/* 等)上的静态资源。另见How to prevent static resources from being handled by front controller servlet which is mapped on /*。需要注意的是 Spring MVC 有一个内置的静态资源 servlet,所以如果你在 Spring 中为静态资源配置一个通用的 URL 模式,你可以将它的前端控制器映射到 /。另见How to handle static content in Spring MVC?

    【讨论】:

    • 谢谢。经过一番研究,我想澄清一个微妙的观点。 / 覆盖 Web 服务器安装的默认 servlet。例如,Tomcat 安装了一个提供静态资源的 DefaultServlet。使用 / 摆脱默认 servlet 作为(很可能是不受欢迎的)副作用。
    • 好吧,我不会称之为“覆盖”,而是“替换”。像这样替换默认的 servlet 会很有用。
    • 抛出错误:Invalid in servlet mapping
    • @BalusC,你能告诉我/** 模式是什么意思吗?
    • 我相信这个答案是不正确的。 /* 不会覆盖所有其他 servlet,并且并非所有请求都最终到达那里。容器将首先尝试找到一个完全匹配,然后是最长的路径前缀匹配。因此,映射为/foo/bar 的servlet 或映射为/foo/* 的servlet 将在映射为/* 的servlet 之前触发。但是,带有/* 的那个将覆盖任何扩展映射,例如*.html
    【解决方案2】:

    /*/ 之间的本质区别在于,映射为/* 的servlet 将在任何带有扩展映射(如*.html)的servlet 之前被选择,而映射为/ 的servlet 将被选中仅在考虑了扩展映射后才被选中(并将用于与其他任何内容都不匹配的任何请求——它是“默认 servlet”)。

    特别是,/* 映射将始终在 / 映射之前被选择。两者都可以防止任何请求到达容器自己的默认 servlet。

    只有在完全匹配的 servlet 映射(如/foo/bar)和长于/* 的路径映射(如/foo/*)之后才会选择其中一个。请注意,空字符串映射与上下文根 (http://host:port/context/) 完全匹配。

    请参阅 Java Servlet 规范的第 12 章,该规范在 3.1 版中提供,地址为 http://download.oracle.com/otndocs/jcp/servlet-3_1-fr-eval-spec/index.html

    【讨论】:

      【解决方案3】:

      也许您还需要知道 url 是如何映射的,因为我遭受了 404 几个小时。有两种处理请求的处理程序。 BeanNameUrlHandlerMappingSimpleUrlHandlerMapping。当我们定义servlet-mapping 时,我们使用的是SimpleUrlHandlerMapping。我们需要知道的一件事是这两个处理程序共享一个名为alwaysUseFullPath 的公共属性,默认为false

      false 这里意味着 Spring 不会使用完整路径将 url 映射到控制器。这是什么意思?这意味着当你定义一个servlet-mapping:

      <servlet-mapping>
          <servlet-name>viewServlet</servlet-name>
          <url-pattern>/perfix/*</url-pattern>
      </servlet-mapping>
      

      处理程序实际上将使用* 部分来查找控制器。例如,当您使用/perfix/api/feature/doSomething 请求时,以下控制器将面临404 错误

      @Controller()
      @RequestMapping("/perfix/api/feature")
      public class MyController {
          @RequestMapping(value = "/doSomething", method = RequestMethod.GET) 
          @ResponseBody
          public String doSomething(HttpServletRequest request) {
              ....
          }
      }
      

      这是一个完美的匹配,对吧?但是为什么404。如前所述,alwaysUseFullPath 的默认值为 false,这意味着在您的请求中,只有/api/feature/doSomething 用于查找对应的 Controller,但没有 Controller 关心该路径。您需要将您的 url 更改为 /perfix/perfix/api/feature/doSomething 或从 MyController 基础 @RequestingMapping 中删除 perfix

      【讨论】:

        【解决方案4】:

        我想用映射规则和一个例子来补充 BalusC 的答案。

        Servlet 2.5 规范的映射规则:

        1. 映射确切的 URL
        2. 映射通配符路径
        3. 地图扩展
        4. 映射到默认 servlet

        在我们的示例中,有三个 servlet。 / 是我们安装的默认servlet。 Tomcat 安装了两个 servlet 来为 jsp 和 jspx 提供服务。所以要映射http://host:port/context/hello

        1. 没有安装确切的 URL servlet,下一步。
        2. 没有安装通配符路径 servlet,下一步。
        3. 不匹配任何扩展,下一步。
        4. 映射到默认 servlet,返回。

        映射http://host:port/context/hello.jsp

        1. 没有安装确切的 URL servlet,下一步。
        2. 没有安装通配符路径 servlet,下一步。
        3. 找到扩展 servlet,返回。

        【讨论】:

          【解决方案5】:

          我认为 Candy 的回答基本正确。有一小部分我不这么认为。

          映射host:port/context/hello.jsp

          1. 没有安装确切的 URL servlet,下一步。
          2. 找到通配符路径 servlet,返回。

          我相信为什么 "/*" 不匹配 host:port/context/hello 是因为它将 "/hello" 视为路径而不是文件(因为它没有扩展名)。

          【讨论】:

            猜你喜欢
            • 2012-04-04
            • 2011-05-12
            • 2017-03-27
            相关资源
            最近更新 更多