Servlet 映射 url 模式中/和/* 的区别

熟悉的代码:

<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是如何映射的吗?

193121 次浏览

<url-pattern>/*</url-pattern>

Servlet 上的 /*覆盖所有其他 servlet,包括 servlet 容器提供的所有 servlet,如缺省 servlet 和 JSP servlet。无论您激发什么请求,它都将最终出现在该 servlet 中。因此,这对于 servlet 来说是一个糟糕的 URL 模式。通常,您希望仅在 Filter上使用 /*。它能够通过调用 FilterChain#doFilter()让请求继续到侦听更具体 URL 模式的任何 servlet。

<url-pattern>/</url-pattern>

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

<url-pattern></url-pattern>

然后还有空字符串 URL 模式 。这将在请求上下文根时调用。这与 <welcome-file>方法不同,<welcome-file>方法在请求任何子文件夹时都不会调用它。这很可能是您实际上正在寻找的 URL 模式,以防您想要一个“ 首页 servlet”。我只能承认,我直觉上认为空字符串 URL 模式 和斜杠 URL 模式 /的定义完全相反,所以我可以理解,许多初学者在这一点上感到困惑。但事已至此。

前台总监

如果您打算使用前端控制器 servlet,那么您最好将它映射到更具体的 URL 模式,如 *.html*.do/pages/*/app/*等。您可以隐藏前端控制器 URL 模式,并在 servlet 过滤器的帮助下覆盖常见 URL 模式(如 /resources/*/static/*等)上的静态资源。参见 如何防止由映射到/* 的前端控制器 servlet 处理静态资源。需要注意的是,Spring MVC 有一个内置的静态资源 servlet,因此如果您在 Spring 中为静态资源配置了一个通用的 URL 模式,那么您可以将其前端控制器映射到 /上。参见 如何在 Spring MVC 中处理静态内容?

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

Servlet 2.5规范中的映射规则:

  1. 地图精确地址
  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,返回。

我认为 Candy 的回答大部分是正确的,但有一小部分我不这么认为。

映射主机: port/context/hello.jsp

  1. 没有精确的 URL servlet 安装,接下来。
  2. 找到通配符路径 servlet ,返回。

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

也许您还需要知道 urls 是如何映射的,因为我已经忍受了几个小时的 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

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

特别是,在 /映射之前总是选择 /*映射。使用这两种方法中的任何一种都可以防止任何请求到达容器自己的默认 servlet。

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

请参阅 JavaServlet 规范的第12章,可在 http://download.oracle.com/otndocs/jcp/servlet-3_1-fr-eval-spec/index.html的3.1版本中获得。