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

192

熟悉的代码:

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

5个回答

297

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

在Servlet中,/*的URL模式会覆盖所有其他的Servlet,包括Servlet容器提供的默认Servlet和JSP Servlet。无论发出什么请求,它都将结束于该Servlet。因此,对于Servlet来说,这是一个不好的URL模式。通常情况下,你只应该在Filter上使用/*。Filter可以通过调用FilterChain#doFilter()让请求继续到任何更具体的URL模式上的Servlet。

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

在Servlet中,/的URL模式是指定一个Servlet作为Web应用程序的默认Servlet。当请求没有匹配到任何其他Servlet或静态资源时,它将被重定向到该Servlet,以便处理请求。通常情况下,你只需要在Web应用程序中指定一个默认Servlet即可。

/ 并不会覆盖其他的servlet。它只会替换servlet容器默认的servlet,用于处理所有不匹配其他已注册servlet的请求。通常这只会应用在静态资源(CSS/JS/image等)和目录列表上。servlet容器默认的servlet也能够处理HTTP缓存请求、媒体(音频/视频)流和文件下载断点续传等功能。一般而言,你不会想要覆盖默认的servlet,因为那样就需要负责它的所有任务,这并不是轻松的(JSF实用库OmniFaces有一个开源示例)。因此,这也是一个不好的servlet URL模式。至于为什么JSP页面不会命中该servlet,是因为servlet容器自带的JSP servlet将被调用,它已经默认映射到更具体的URL模式*.jsp

还有一个空字符串URL模式 。当请求上下文根时,将调用此模式。这与<welcome-file>的方法不同,因为当请求任何子文件夹时不会调用它。如果您想要一个“主页servlet”,那么这很可能是您实际寻找的URL模式。我只能承认,我直觉地期望空字符串URL模式 和斜杠URL模式/定义完全相反,因此我可以理解许多初学者对此感到困惑。但事情就是这样。

前置控制器

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


10
谢谢。经过一番调查,我想澄清一个微妙的问题。在Web服务器中,反斜杠“/”会覆盖默认安装的Servlet。例如,Tomcat会安装一个DefaultServlet来提供静态资源。使用“/”将作为(很可能是不希望的)副作用消除默认Servlet。 - Candy Chiu
1
<url-pattern></url-pattern> 抛出错误:servlet 映射中的无效 <url-pattern>。 - slim
2
@BalusC,你能告诉我 /** 模式表示什么吗? - Sajib Acharya
1
@Sajib:这不是Servlet API的一部分。一个好的容器应该会在这方面抛出部署异常。然而,/**后缀模式可以从第三方库/框架(如Spring和Shiro)中识别。请查阅其文档以获取详细信息。 - BalusC
1
我认为这个答案是不正确的。/*并不会覆盖所有其他的servlet,也不是所有的请求都会到达那里。容器首先会尝试找到一个精确匹配,然后是最长路径前缀匹配。因此,具有映射/foo/bar或具有映射/foo/*的servlet将在具有映射/*的servlet之前触发。但是,具有/*的servlet将覆盖任何扩展映射,如*.html - Robert Tupelo-Schneck
显示剩余3条评论

50

我想用映射规则和示例来补充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,返回结果。

26

也许您需要知道如何映射URL,因为我曾经遭受过长达数小时的404错误。有两种处理程序处理请求:BeanNameUrlHandlerMappingSimpleUrlHandlerMapping。当我们定义一个时,我们使用的是SimpleUrlHandlerMapping。需要知道的一件事是这两个处理程序共享一个名为alwaysUseFullPath的属性,默认值为false

这里的false意味着Spring不会使用完整路径将URL映射到控制器。这是什么意思?这意味着当您定义一个时:

<servlet-mapping>
    <servlet-name>viewServlet</servlet-name>
    <url-pattern>/perfix/*</url-pattern>
</servlet-mapping>
处理程序将实际使用 * 部分来查找控制器。例如,当您使用 /prefix/api/feature/doSomething 请求以下控制器时,它将面临 404 错误。

处理程序将实际使用*部分来查找控制器。例如,当您使用/prefix/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来查找相应的控制器,但没有一个控制器关心该路径。您需要将您的URL更改为/perfix/perfix/api/feature/doSomething或从MyController的基本@RequestingMapping中删除perfix


9

我认为Candy的回答大部分是正确的。但是有一小部分我持不同意见。

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

  1. 没有安装精确的URL servlets,继续下一步。
  2. 找到通配符路径servlets,返回结果。

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


3
/*/ 之间的本质区别在于具有映射 /* 的 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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接