这一节虽然简单,但是很繁琐,可以先了解 @RequestMapping 的使用,不是很明白也没有关系,先继续往下学习,等你回头再看一遍的时候,你会发现 @RequestMapping 竟然是如此的简单!
@RequestMapping可以在控制器类上或者控制器方法上使用。
在类的级别上的注解会将一个特定请求或者请求模式映射到一个控制器之上。之后你还可以另外添加方法级别的注解来进一步指定到处理方法的映射关系。
基础用法:
下面的 @RequestMapping("/index") 等同于 @RequestMapping(value = "/index")
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/index")
public class HelloWorldController {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello() {
return "/WEB-INF/views/success.jsp";
}
@RequestMapping(value = "/world", method = RequestMethod.POST)
public String world() {
return "/WEB-INF/views/success.jsp";
}
}
method
参数支持:GET, PUT, POST, DELETE 以及 PATCH。使用 method
可以限制接受的请求类型。
hello() 方法将只接受请求方式为 GET 方式,请求地址为:/index/hello 的请求。
world() 方法将只接受请求方式为 POST 方式,请求地址为:/index/world 的请求。
@GetMapping("/hello") 等同于 @RequestMapping(value="/hello", method=RequestMethod.GET)
@PostMapping("/world") 等同于 @RequestMapping(value="/world", method=RequestMethod.POST)
映射多个地址:
@RequestMapping
还可以将多个请求映射到一个方法上,只需要给 value
来指定一个包含多个路径的列表。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/index")
public class HelloWorldController {
@RequestMapping(value = {"/hello", "/world", "/helloworld"})
public String hello() {
return "/WEB-INF/views/success.jsp";
}
}
URI模板:
URI模板可以为快速访问 @RequestMapping
中指定的URL的一个特定的部分提供很大的便利。
使用 @PathVariable
可以获取到 {name} 的值,并在控制台进行输出。比如请求地址为:http://localhost:8080/SpringMVC/hello/jack 那么控制台上将会把 jack 进行输出。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorldController {
@RequestMapping("/hello/{name}")
public String hello(@PathVariable String name) {
System.out.println(name);
return "/WEB-INF/views/success.jsp";
}
}
如果路径中的URI变量和方法中的参数名不一样的话,那么需要在 @PathVariable
中显示的绑定参数。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorldController {
@RequestMapping("/hello/{name}")
public String hello(@PathVariable("name") String username) {
System.out.println(username);
return "/WEB-INF/views/success.jsp";
}
}
一个方法可以拥有任意数量的 @PathVariable
注解:
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorldController {
@RequestMapping("/hello/{name}/age/{age}")
public String hello(@PathVariable String name, @PathVariable int age) {
System.out.println("name:" + name + ",age:" + age);
return "/WEB-INF/views/success.jsp";
}
}
@PathVariable
可以被应用于所有 简单类型 的参数上,比如 int、long、Date 等类型。Spring会自动地帮你把参数转化成合适的类型,如果转换失败,就抛出一个 TypeMismatchException
。如果你需要处理其他数据类型的转换,也可以注册自己的类。
带正则表达式的URI模板:
你可以使用正则表达式来准确的描述可以接受的请求路径:
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorldController {
@RequestMapping("/hello/{name:[a-z]+}/age/{age}")
public String hello(@PathVariable String name, @PathVariable int age) {
System.out.println("name:" + name + ",age:" + age);
return "/WEB-INF/views/success.jsp";
}
}
Ant风格的路径模式:
除了URI模板外,@RequestMapping
注解还支持Ant风格的路径模式(如/hello/*.do
等)。不仅如此,还可以把URI模板变量和Ant风格的glob组合起来使用(比如/hello/*/user/{userId}
这样的用法等)。其中*则表示任意字符串。但是遇到 / 那么就会认为是下一部分的URI,所以 * 中不能有 / 。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorldController {
// 匹配的地址:http://localhost:8080/SpringMVC/hello/jack/user/18
@RequestMapping("/hello/*/user/{userId}")
public String hello(@PathVariable String userId) {
System.out.println(userId);
return "/WEB-INF/views/success.jsp";
}
}
那么想要匹配带 / 的路径那该怎么办?可以使用 ** 来进行匹配。例如 /hello/**/user/{userId} 则会匹配 /hello/ 和 /user/{userId} 之间的部分。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorldController {
// 匹配的地址:http://localhost:8080/SpringMVC/hello/jack/tom/cat/user/18
@RequestMapping("/hello/**/user/{userId}")
public String hello(@PathVariable String userId) {
System.out.println(userId);
return "/WEB-INF/views/success.jsp";
}
}
路径样式的匹配(Path Pattern Comparison):
当一个URL同时匹配多个模板(pattern)时,我们将需要一个算法来决定其中最匹配的一个。
URI模板变量的数目和通配符数量的总和最少的那个路径模板更准确。举个例子,/hotels/{hotel}/*
这个路径拥有一个URI变量和一个通配符,而/hotels/{hotel}/**
这个路径则拥有一个URI变量和两个通配符,因此,我们认为前者是更准确的路径模板。
如果两个模板的URI模板数量和通配符数量总和一致,则路径更长的那个模板更准确。举个例子,/foo/bar*
就被认为比/foo/*
更准确,因为前者的路径更长。
如果两个模板的数量和长度均一致,则那个具有更少通配符的模板是更加准确的。比如,/hotels/{hotel}
就比/hotels/*
更精确。
除此之外,还有一些其他的规则:
- 默认的通配模式
/**
比其他所有的模式都更“不准确”。比方说,/api/{a}/{b}/{c}
就比默认的通配模式/**
要更准确 - 前缀通配(比如
/public/**
)被认为比其他任何不包括双通配符的模式更不准确。比如说,/public/path3/{a}/{b}/{c}
就比/public/**
更准确
更多的细节请参考这两个类:AntPatternComparator
和AntPathMatcher
。值得一提的是,PathMatcher类是可以配置的。
后缀模式匹配:
Spring MVC默认采用 ".*"
的后缀模式匹配来进行路径匹配,因此,一个映射到/person
路径的控制器也会隐式地被映射到 /person.*
。这使得通过URL来请求同一资源文件的不同格式变得更简单(比如 /person.pdf
,/person.xml
)。
关闭后缀模式匹配:
java:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer
// 关闭后缀模式匹配
.setUseSuffixPatternMatch(false)
.setUseTrailingSlashMatch(false)
.setUseRegisteredSuffixPatternMatch(true)
.setPathMatcher(antPathMatcher())
.setUrlPathHelper(urlPathHelper());
}
@Bean
public UrlPathHelper urlPathHelper() {
//...
}
@Bean
public PathMatcher antPathMatcher() {
//...
}
}
xml:
<mvc:annotation-driven>
<mvc:path-matching
<!-- 关闭后缀模式匹配 -->
suffix-pattern="false"
trailing-slash="false"
registered-suffixes-only="true"
path-helper="pathHelper"
path-matcher="pathMatcher"/>
</mvc:annotation-driven>
<bean id="pathHelper" class="org.example.app.MyPathHelper"/>
<bean id="pathMatcher" class="org.example.app.MyPathMatcher"/>
矩阵变量:
矩阵变量可以在任何路径段落中出现,每对矩阵变量之间使用一个分号 “;” 隔开。比如这样的URI:"/cars;color=red;year=2012"
。多个值可以用逗号隔开 "color=red,green,blue"
,或者重复变量名多次 "color=red;color=green;color=blue"
。
如果一个URL有可能需要包含矩阵变量,那么在请求路径的映射配置上就需要使用URI模板来体现这一点。这样才能确保请求可以被正确地映射,而不管矩阵变量在URI中是否出现、出现的次序是怎样等。在方法参数中使用 @MatrixVariable
来获得矩阵变量中的值。
使用矩阵变量之前,需要在 springmvc 配置文件中开启自动解析矩阵变量:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven enable-matrix-variables="true"/>
</beans>
下面的代码使用了矩阵变量:
使用 /pets/42;q=11;r=22
路径来请求之后,控制台上会输出 petId:42,q:11
。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class MatrixController {
@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET)
public void findPet(@PathVariable String petId, @MatrixVariable(name = "q", pathVar = "petId") int q) {
System.out.println("petId:" + petId + "," + "q:" + q);
}
}
由于任意路径段落中都可以含有矩阵变量,在某些场景下,你需要用更精确的信息来指定一个矩阵变量的位置:
使用 /owners/42;q=11/pets/21;q=22
路径来请求之后,控制台上会输出 q1:11,q2:22
。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class MatrixController {
@RequestMapping(path = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)
public void findPet(@MatrixVariable(name = "q", pathVar = "ownerId") int q1,
@MatrixVariable(name = "q", pathVar = "petId") int q2) {
System.out.println("q1:" + q1 + "," + "q2:" + q2);
}
}
你也可以声明一个矩阵变量不是必须出现的,并给它赋一个默认值:
使用 /pets/42
路径来请求之后,控制台上会输出 q:1
。
package com.pudding.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class MatrixController {
@RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET)
public void findPet(@MatrixVariable(required = false, defaultValue = "1") int q) {
System.out.println("q:" + q);
}
}
可以观察到,我们请求的路径中并没有 q 这个参数,配置了 required = false, defaultValue = "1"
之后,如果路径中没有指定的矩阵变量,那么 SpringMVC 会自动给矩阵变量设置默认值。
也可以通过一个Map来存储所有的矩阵变量:
使用 /owners/42;q=11;r=12/pets/21;q=22;s=23
路径来请求之后,控制台上会输出 matrixVars:{q=11, r=12, s=23} 和 petMatrixVars:{q=22, s=23}
。
package com.pudding.controller;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.MatrixVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class MatrixController {
@RequestMapping(path = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)
public void findPet(@MatrixVariable Map<String, String> matrixVars,
@MatrixVariable(pathVar = "petId") Map<String, String> petMatrixVars) {
System.out.println("matrixVars:" + matrixVars);
System.out.println("petMatrixVars:" + petMatrixVars);
}
}
consumes:
你可以指定一组可消费的媒体类型,缩小映射的范围。这样只有当请求头中 Content-Type 的值与指定可消费的媒体类型中有相同的时候,请求才会被匹配。比如下面这个例子:
@Controller
@RequestMapping(path = "/pets", method = RequestMethod.POST, consumes="application/json")
public void addPet(@RequestBody Pet pet, Model model) {
// 方法实现省略
}
指定可消费媒体类型的表达式中还可以使用否定,比如,可以使用 !text/plain 来匹配所有请求头 Content-Type 中不含 text/plain 的请求。同时,在MediaType
类中还定义了一些常量,比如APPLICATION_JSON_VALUE
、APPLICATION_JSON_UTF8_VALUE
等,推荐更多地使用它们。
produces:
你可以指定一组可生产的媒体类型,缩小映射的范围。这样只有当请求头中 Accept 的值与指定可生产的媒体类型中有相同的时候,请求才会被匹配。而且,使用 produces 条件可以确保用于生成响应(response)的内容与指定的可生产的媒体类型是相同的。举个例子:
@RestController
@RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Pet getPet(@PathVariable String petId, Model model) {
// 方法实现省略
}
与 consumes 条件类似,可生产的媒体类型表达式也可以使用否定。比如,可以使用 !text/plain 来匹配所有请求头 Accept 中不含 text/plain 的请求。同时,在MediaType
类中还定义了一些常量,比如APPLICATION_JSON_VALUE
、APPLICATION_JSON_UTF8_VALUE
等,推荐更多地使用它们。
params:
你可以筛选请求参数的条件来缩小请求匹配范围,比如"myParam"
、"!myParam"
及"myParam=myValue"
等。前两个条件用于筛选存在/不存在某些请求参数的请求,第三个条件筛选具有特定参数值的请求。下面有个例子,展示了如何使用请求参数值的筛选条件:
发送请求时必须带上名字为myParam
的参数,否则会报错Parameter conditions "myParam" not met for actual request parameters:
@Controller
public class MyParamDemoController {
@RequestMapping(value = "/param-demo", method = RequestMethod.GET, params = "myParam")
public void paramDemo() {
// 省略方法体
}
}
发送请求时不匹配带有myParam
的参数的请求,否则会报错Parameter conditions "!myParam" not met for actual request parameters: myParam={helloworld}
@Controller
public class MyParamDemoController {
@RequestMapping(value = "/param-demo", method = RequestMethod.GET, params = "!myParam")
public void paramDemo() {
// 省略方法体
}
}
可以用相同的条件来筛选请求头的出现与否,或者筛选出一个具有特定值的请求头,否则会报错Parameter conditions "myParam=helloworld" not met for actual request parameters: myParam={123}
@Controller
public class MyParamDemoController {
@RequestMapping(value = "/param-demo", method = RequestMethod.GET, params = "myParam=helloworld")
public void paramDemo() {
// 方法体省略
}
}
同样的,params
参数支持String类型的数组,这意味着你可以指定多个参数的限制:
@Controller
public class MyParamDemoController {
@RequestMapping(value = "/param-demo", method = RequestMethod.GET, params = { "myParam=helloworld", "myParam2" })
public void paramDemo() {
// 省略方法体
}
}
headers:
限制请求头的参数
限制请求头的参数User-Agent
来让谷歌浏览器不能访问:
@Controller
public class MyHeaderDemoController {
@RequestMapping(value = "/headers-demo", method = RequestMethod.GET, headers = "User-Agent!=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36")
public void headersDemo() {
// 省略方法体
}
}
注:以上资料部分参考自W3Cschool翻译的SpringMVC官方文档