在web.xml中,我们通过下面的标签来匹配客户端发出的请求:
<servlet-mapping>
<servlet-name>yzh_ssh</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
1、匹配顺序
该标签处理客户端请求的顺序为:
(1)完全匹配
<url-pattern>/test/userList.action</url-pattern>
完全匹配就是等值匹配,它是最先被匹配到的客户端请求。
(2)路径匹配
<url-pattern>/*</url-pattern>
匹配根路径下的全部请求
路径匹配类似于模糊匹配,对于/list、listInfo.do、checkInfo.action、index.jsp、abc.css、my.js
等等url请求,都可以被匹配到。这种匹配的顺序排在完全匹配后面。
(3)扩展名匹配
<url-pattern>*.action</url-pattern>
匹配全部action 结尾的请求
<url-pattern>*.css</url-pattern>
匹配全部 css结尾的请求
<url-pattern>*</url-pattern>
不能用 * ,否则报错
说明:这种匹配匹配在开发中用的比较多,但不能使用*代替,否则会报错。
结论:容器会首先查找完全匹配,如果找不到,再查找目录匹配,如果也找不到,就查找扩展名匹配。
2、“/” 与 “/*” 的区别
(1)<url-pattern>/</url-pattern>
会匹配到 /toIndex 这种类型的 url ,不会匹配到模式为 *.jsp 这样的后缀型 url。
(2)<url-pattern>/*</url-pattern>
会匹配所有路径型的和后缀型的url。
3、选用/还是.action作为请求路径匹配?
我个人认为,我们选用.action作为请求路径会比较好。为什么呢?我们先使用/来处理下面的问题,假设有两个controller层的方法,如下所示。
@ResponseBody
@RequestMapping(value = "/listUserInfos",method=RequestMethod.GET)
public DataMessage listUserInfos(Pagination<UserInfo> pageParams) throws Exception {
userInfoService.queryPage(pageParams);
return DataMessage.successData(pageParams);
}
@GetMapping(value = "/toIndex")
public String toIndex(){
return "/index";
}
说明:这两个方法之间的关系是通过toIndex()来方法listUserInfos()这个方法,其中toIndex()来方法返回一个视图。启动程序,我们在浏览器中输入下面的请求路径:
http://localhost:8080/hBSpringMvcTestProSwagger/list/listUserInfos.action
我们发现。可以在浏览器上方显示出下面的信息:
{"type":"success","content":"操作成功","data":{"pageNo":1,"pageSize":15,"totalPage":1,"params":{},"total":3,"rows":[{"userName":"yzh","password":"123","age":23,"sex":"男","tel":"12345678","email":"123@qq.com"},{"userName":"lhy","password":"1257","age":20,"sex":"女","tel":"12345678","email":"1256@qq.com"},{"userName":"tom","password":"12312","age":21,"sex":"男","tel":"12345678","email":"1231@qq.com"}]}}
接着,我们输入下面的url请求路径:
http://localhost:8080/hBSpringMvcTestProSwagger/list/toIndex.action
这是什么原因呢?前面不是说/*可以任何请求吗?当匹配到客户端发出一个toIndex.action请求,会被*匹配到,因此会访问到toIndex这个controller方法,此时会一个视图,而返回jsp视图时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错。那么可以采取什么办法解决这个问题呢?实际上,我们可以这样来解决这个问题:
<servlet-mapping>
<servlet-name>yzh_ssh</servlet-name>
<url-pattern>/base/*</url-pattern>
</servlet-mapping>
这个时候,请求路径就会匹配url中带base的请求,这样一来,我们将所有访问controller的请求都设置一个base基础目录,就像下面这样:
http://localhost:8080/hBSpringMvcTestProSwagger/base/test/toIndex.action
而对于返回的视图是index.jsp,因为该目录中并没有设置base基础目录,因此将无法匹配到controller方法,这时,如果发现是没有经过映射处理的请求,将该请求交由 WEB 应用服务器默认的Servlet处理。 如果不是静态资源的请求, 才由 DispatcherServlet 继续处理 一般 WEB 应用服务器默认的 Servlet 的名称都是 default。 若所使用的WEB 服务器默认的Servlet 名称不是default, 则需要通过default-servlet-name 属性显式指定,可以使用<mvc:default-servlet-handler/>
并结合<mvc:annotation-driven></mvc:annotation-driven>
处理这个静态资源的问题。使用这种方法的优点是能够支持第三方插件的类似/*
的请求,例如Swagger中的http://localhost:8998/swagger-resources/configuration/ui
这种请求,如果配置*.action是无法筛选到controller层的。
(2)*.action方式
*.action会筛选出后缀为action的请求,因此在返回的视图是index.jsp时,无法匹配到controller方法,这时,如果发现是没有经过映射处理的请求,将该请求交由 WEB 应用服务器默认的Servlet处理,从而展示出效果。但是这种方式也有一个缺点,并不支持Swagger中的http://localhost:8998/swagger-resources/configuration/ui
这种请求(不是静态资源),如果配置*.action
是无法筛选到controller层的,从而显示404的错误。但是我们也有办法解决这个问题,实际上,我们可以定义多个前端控制器来分发处理这些不同格式的请求,例如:
<!-- 配置DispatcherServlet -->
<servlet>
<servlet-name>yzh_ssh1</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置springMVC的配置文件 -->
<!-- 如果不配置下面选项,系统默认加载classpath下面名为[servlet-name]-servlet.xml的文件 springmvc01-servlet.xml -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<!-- 过滤controller层请求,只能通过*.action请求 -->
<servlet-mapping>
<servlet-name>yzh_ssh1</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>yzh_ssh2</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置springMVC的配置文件 -->
<!-- 如果不配置下面选项,系统默认加载classpath下面名为[servlet-name]-servlet.xml的文件 springmvc01-servlet.xml -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>yzh_ssh2</servlet-name>
<url-pattern>/base/*</url-pattern>
</servlet-mapping>
说明:我们看到,在上面我们定义了两个前端控制器分别处理不同格式的请求,前端控制器yzh_ssh1
用于处理后缀为action的请求,而前端控制器yzh_ssh2
用于处理包含/base/*
的请求,符合该请求的,会将*
号后面的url与controller的映射url相匹配。
结论:如果只涉及到controller业务请求的,建议使用*.action
的请求筛选方式,如果是既有\xxx
这种第三方插件请求方式,又有controller业务请求方式,那么就可以考虑使用\*
这种筛选方式。