Servlet中有几个Path的概念

ContextPath

javax.servlet.ServletContext#getContextPath

  1. /**
  2. * Returns the context path of the web application.
  3. *
  4. * <p>The context path is the portion of the request URI that is used
  5. * to select the context of the request. The context path always comes
  6. * first in a request URI. If this context is the “default” context
  7. * rooted at the base of the Web server’s URL name space, this path
  8. * will be an empty string. Otherwise, if the context is not rooted at
  9. * the root of the server’s name space, the path starts with a /
  10. * character but does not end with a / character.
  11. *
  12. * <p>It is possible that a servlet container may match a context by
  13. * more than one context path. In such cases the
  14. * {@link javax.servlet.http.HttpServletRequest#getContextPath()}
  15. * will return the actual context path used by the request and it may
  16. * differ from the path returned by this method.
  17. * The context path returned by this method should be considered as the
  18. * prime or preferred context path of the application.
  19. *
  20. * @return The context path of the web application, or "" for the
  21. * default (root) context
  22. *
  23. * @see javax.servlet.http.HttpServletRequest#getContextPath()
  24. *
  25. * @since Servlet 2.5
  26. */
  27. public String getContextPath();

一般Web容器实现上支持运行多个App,而Context path就是用来隔离App。
常见的应用里,servlet context path都用配置为:/, 用于表示Web 访问的URL的第一级路径。例如本地部署War到Tomcat或Jetty中,指定的context path为工程名称,那么访问时需要带上工程名称:

http:localhost:8080/${project}/${controller}

ServletPath

javax.servlet.http.HttpServletRequest#getServletPath

  1. /**
  2. * Returns the part of this request's URL that calls
  3. * the servlet. This path starts with a "/" character
  4. * and includes either the servlet name or a path to
  5. * the servlet, but does not include any extra path
  6. * information or a query string. Same as the value of
  7. * the CGI variable SCRIPT_NAME.
  8. *
  9. * <p>This method will return an empty string ("") if the
  10. * servlet used to process this request was matched using
  11. * the "/*" pattern.
  12. *
  13. * @return a <code>String</code> containing
  14. * the name or path of the servlet being
  15. * called, as specified in the request URL,
  16. * decoded, or an empty string if the servlet
  17. * used to process the request is matched
  18. * using the "/*" pattern.
  19. */
  20. public String getServletPath();

ServletPath一般用于配置在Servlet的映射URL中,用来匹配Servlet的。

  1. <servlet>
  2. <servlet-name>backend</servlet-name>
  3. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  4. <init-param>
  5. <param-name>contextConfigLocation</param-name>
  6. <param-value>classpath:web/webmvc-backend.xml</param-value>
  7. </init-param>
  8. <load-on-startup>0</load-on-startup>
  9. </servlet>
  10. <servlet-mapping>
  11. <servlet-name>backend</servlet-name>
  12. <url-pattern>/backend/*</url-pattern>
  13. </servlet-mapping>

如果访问/backend/xxx开头的资源,则会匹配到backend这个servlet.

getPathInfo

简单来说:就是servlet path后,查询参数前的一段路径。

  1. /**
  2. * Returns any extra path information associated with
  3. * the URL the client sent when it made this request.
  4. * The extra path information follows the servlet path
  5. * but precedes the query string and will start with
  6. * a "/" character.
  7. *
  8. * <p>This method returns <code>null</code> if there
  9. * was no extra path information.
  10. *
  11. * <p>Same as the value of the CGI variable PATH_INFO.
  12. *
  13. * @return a <code>String</code>, decoded by the
  14. * web container, specifying
  15. * extra path information that comes
  16. * after the servlet path but before
  17. * the query string in the request URL;
  18. * or <code>null</code> if the URL does not have
  19. * any extra path information
  20. */
  21. public String getPathInfo();

RequestUri

RequestUri = Context Path + Servlet path + Path info
说明如下:

  1. |-- Context Path --|-- Servlet Path -|--Path Info--|------------|
  2. http://www.myserver.com /mywebapp /helloServlet /hello |?a=x&b=y |
  3. |-------- Request URI ----------------------------|Query String|

Remember the following three points:

  1. Request URI = context path + servlet path + path info.
    2. Context paths and servlet paths start with a / but do not end with it.
    3. HttpServletRequest provides three methods getContextPath(),
    getServletPath() and getPathInfo() to retrieve the context path,
    the servlet path, and the path info, respectively, associated with a request.

Identifying the servlet path

  • 容器尝试查找请求路径与servlet的精确匹配;
  • 容器会递归地尝试匹配最长路径前缀。以/为分隔符,在路径树中一次步进一个目录。最长的匹配会决定由哪个servlet处理;
  • 如果URL路径中的最后一段包含扩展名(如.jsp),那么容器会尝试匹配能处理扩展名的servlet。扩展名定义为最后一段中最后的点号(.)之后的部分;
  • 如果前三个规则没有成功匹配,容器会尝试去为所请求的资源提供服务。如果为应用定义了默认servlet,则它会被使用。许多容器都提供了隐式的默认servlet。

附原文:
To match a request URI with a servlet, the servlet container follows a simple algorithm.
Once it identifies the context path, if any, it evaluates the remaining part of the
request URI with the servlet mappings specified in the deployment descriptor, in the
following order. If it finds a match at any step, it does not take the next step.

1 The container tries to match the request URI to a servlet mapping. If it finds a
match, the complete request URI (except the context path) is the servlet path. In
this case, the path info is null.
2 It tries to recursively match the longest path by stepping down the request URI
path tree a directory at a time, using the / character as a path separator, and determining
if there is a match with a servlet. If there is a match, the matching part
of the request URI is the servlet path and the remaining part is the path info.
3 If the last node of the request URI contains an extension (.jsp, for example),
the servlet container tries to match it to a servlet that handles requests for the
specified extension. In this case, the complete request URI is the servlet path
and the path info is null.
4 If the container is still unable to find a match, it will forward the request to the
default servlet. If there is no default servlet, it will send an error message indicating
the servlet was not found.

Servlet path match example

web.xml example

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://java.sun.com/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
  5. http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  6. version="3.0">
  7. <display-name>springmvc-demo</display-name>
  8. <context-param>
  9. <param-name>appName</param-name>
  10. <param-value>SpringMVCDemo</param-value>
  11. </context-param>
  12. <servlet>
  13. <servlet-name>red</servlet-name>
  14. <servlet-class>com.springmvc.demo.web.servlet.RedServlet</servlet-class>
  15. </servlet>
  16. <servlet-mapping>
  17. <servlet-name>red</servlet-name>
  18. <url-pattern>/red</url-pattern>
  19. </servlet-mapping>
  20. <servlet>
  21. <servlet-name>red2</servlet-name>
  22. <servlet-class>com.springmvc.demo.web.servlet.RedServlet2</servlet-class>
  23. </servlet>
  24. <servlet-mapping>
  25. <servlet-name>red2</servlet-name>
  26. <url-pattern>/red/*</url-pattern>
  27. </servlet-mapping>
  28. <servlet>
  29. <servlet-name>blue</servlet-name>
  30. <servlet-class>com.springmvc.demo.web.servlet.BlueServlet</servlet-class>
  31. </servlet>
  32. <servlet-mapping>
  33. <servlet-name>blue</servlet-name>
  34. <url-pattern>/blue</url-pattern>
  35. </servlet-mapping>
  36. <servlet>
  37. <servlet-name>blue2</servlet-name>
  38. <servlet-class>com.springmvc.demo.web.servlet.BlueServlet2</servlet-class>
  39. </servlet>
  40. <servlet-mapping>
  41. <servlet-name>blue2</servlet-name>
  42. <url-pattern>/blue/</url-pattern>
  43. </servlet-mapping>
  44. <servlet>
  45. <servlet-name>def</servlet-name>
  46. <servlet-class>com.springmvc.demo.web.servlet.DefaultServlet</servlet-class>
  47. </servlet>
  48. <servlet-mapping>
  49. <servlet-name>def</servlet-name>
  50. <url-pattern>/*</url-pattern>
  51. </servlet-mapping>
  52. </web-app>

Servlet example:

  1. public class RedServlet extends HttpServlet {
  2. @Override
  3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  4. System.out.println("Show RedServlet Request Info");
  5. System.out.println("ContextPath: " + req.getContextPath());
  6. System.out.println("ServletPath: " + req.getServletPath());
  7. System.out.println("PathInfo: " + req.getPathInfo());
  8. }
  9. }
  10. public class RedServlet2 extends HttpServlet {
  11. @Override
  12. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  13. System.out.println("Show RedServlet2 Request Info");
  14. System.out.println("ContextPath: " + req.getContextPath());
  15. System.out.println("ServletPath: " + req.getServletPath());
  16. System.out.println("PathInfo: " + req.getPathInfo());
  17. }
  18. }
  19. public class BlueServlet extends HttpServlet {
  20. @Override
  21. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  22. System.out.println("Show BlueServlet Request Info");
  23. System.out.println("ContextPath: " + req.getContextPath());
  24. System.out.println("ServletPath: " + req.getServletPath());
  25. System.out.println("PathInfo: " + req.getPathInfo());
  26. }
  27. }
  28. public class BlueServlet2 extends HttpServlet {
  29. @Override
  30. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  31. System.out.println("Show BlueServlet2 Request Info");
  32. System.out.println("ContextPath: " + req.getContextPath());
  33. System.out.println("ServletPath: " + req.getServletPath());
  34. System.out.println("PathInfo: " + req.getPathInfo());
  35. }
  36. }
  37. public class DefaultServlet extends HttpServlet {
  38. @Override
  39. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  40. System.out.println("Show Default Request Info");
  41. System.out.println("RequestURL: " + req.getRequestURI());
  42. System.out.println("ContextPath: " + req.getContextPath());
  43. System.out.println("ServletPath: " + req.getServletPath());
  44. System.out.println("PathInfo: " + req.getPathInfo());
  45. }
  46. }

Test Result

RequestURI ContextPath ServletUsed ServletPath PathInfo
http://localhost:8081/red/test “” RedServlet /red /test
http://localhost:8081/red/ “” RedServlet2 /red /
http://localhost:8081/red/a/b/c “” RedServlet2 /red /a/b/c
http://localhost:8081/blue “” BlueServlet /blue null
http://localhost:8081/blue/ “” BlueServlet2 /blue/ null
http://localhost:8081/blue/test “” Not Found(404)
http://localhost:8081/blue/test/some “” DefaultServlet “” /blue/test/some
http://localhost:8081/aa/bb/cc “” DefaultServlet “” /aa/bb/cc
http://localhost:8081/ “” DefaultServlet “” /

综上:ServletPath以web.xml中配置的url-pattern为基准.如果是/servlet-url/*,则servletpath为servlet-url. 匹配规则是先进行进准的servlet-url匹配,然后匹配路由到/*对应的Servlet.
总结为:url匹配优先级:精确匹配 > 以/开头的匹配 >以*开头的匹配。总之匹配越精确,优先级越高。

关于/与/*

Servlet中url-pattern为/*,此时servletpath为””,表示路径匹配,匹配所有的请求,包含后缀.
在这种url-pattern下,通过路径匹配时,如果请求的是静态资源,没有找到合适的Controller或其他处理器,也会出现找不到资源的问题,此时也需要考虑添加:

如:配置

  1. <!-- servlet映射到springmvc -->
  2. <servlet>
  3. <servlet-name>frontend</servlet-name>
  4. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  5. <init-param>
  6. <param-name>contextConfigLocation</param-name>
  7. <param-value>classpath:web/webmvc-frontend.xml</param-value>
  8. </init-param>
  9. <load-on-startup>0</load-on-startup>
  10. </servlet>
  11. <servlet-mapping>
  12. <servlet-name>frontend</servlet-name>
  13. <url-pattern>/*</url-pattern>
  14. </servlet-mapping>

访问项目中的静态资源:webapp/resources/demo.txt
则会出现404
image.png
此次需要添加: 即可访问
image.png

Servlet中的url-pattern为/,表示覆盖默认的Servlet处理器,此时ServletPath为/,意味着所有的请求如果找不到处理器,最后会路由到默认的Servlet(这种情况下,不会处理*.jsp,访问带jsp后缀的会返回404),配置了/,表示覆盖容器默认的Servlet,可能存在无法访问静态资源的兼容性问题。
在使用DispatchServlet时,如果配置了/作为url-pattern,需要考虑静态资源的兼容性问题,此时可以在容器中添加:

This tag allows for mapping the DispatcherServlet to “/“ (thus overriding the mapping of the container’s default Servlet), while still allowing static resource requests to be handled by the container’s default Servlet. It configures a DefaultServletHttpRequestHandler with a URL mapping (given a lowest precedence order) of “/**”. This handler will forward all requests to the default Servlet.

这样通过DispatcherServlet转交到容器的默认Servlet.
如上述示例,配置为:

  1. <servlet>
  2. <servlet-name>frontend</servlet-name>
  3. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  4. <init-param>
  5. <param-name>contextConfigLocation</param-name>
  6. <param-value>classpath:web/webmvc-frontend.xml</param-value>
  7. </init-param>
  8. <load-on-startup>0</load-on-startup>
  9. </servlet>
  10. <servlet-mapping>
  11. <servlet-name>frontend</servlet-name>
  12. <url-pattern>/</url-pattern>
  13. </servlet-mapping>

不添加 仍然会出现访问静态资源时出现404.而添加上该配置后,即可访问。

下文为一些说明:

As per the Servlet specification, mapping for “/“ means default servlet meaning if there is no explicit servlet matching the request, then this default servlet would be serving the request. For e.g., there is a servlet named “default” defined in Tomcat server common configuration web.xml which is inherited by all applications. This servlet serves the static contents like css,images etc which are typically not mapped in applications web.xml. Similarly there is a special Servlet which handles requests for jsp files ( all request ending with *.jsp as naturally these will be needed to be compiled to Servlets which would then process the request). So if you override the default servlet to be any other servlet in the application web.xml, then all requests not handled by any other servlet goes to this servlet and if this Servlet is not capable to serving request, it will not work. If you declare Spring dispatcher servlet as the default Servlet, then you will not be able to serve static contents from container provided Servlet. Instead there is a special handler provided which can load static resources from configurable path pattern from directory / classpath. You need to use <mvc:resources/> tag for this feature. However if you still want to use container provided Servlet for serving resource you would need to use <mvc:default-servlet-handler/> in the spring configuration. You can read more about this approach and its prons/cons here - section 15.12.4

参考

https://blog.csdn.net/cooljia/article/details/187882