servlet 映射 url 模式中 / 和 /* 之间的区别

Posted

技术标签:

【中文标题】servlet 映射 url 模式中 / 和 /* 之间的区别【英文标题】:Difference between / and /* in servlet mapping url pattern 【发布时间】:2011-05-07 15:04:53 【问题描述】:

熟悉的代码:

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

【问题讨论】:

【参考方案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】:

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

Servlet 2.5 规范的映射规则:

    映射确切的 URL 映射通配符路径 地图扩展 映射到默认 servlet

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

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

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

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

【讨论】:

【参考方案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】:

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

映射host:port/context/hello.jsp

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

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

【讨论】:

【参考方案5】:

/*/ 的本质区别在于,映射为/* 的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。

【讨论】:

以上是关于servlet 映射 url 模式中 / 和 /* 之间的区别的主要内容,如果未能解决你的问题,请参考以下文章

在 Spring MVC 的 servlet 映射中,如何映射 url 模式目录的根目录?

带有 url 模式“/ *”的 servlet 映射中的 ***Error

servlet 映射 url 模式上的双通配符 (*) 是啥意思?

过滤器映射 URL 模式 *.action 不起作用 struts2

Java Servlet URL 映射

java servlet url patterns 路径匹配