一文了解 Spring RequestMapping

1. 概述
 
这篇文章会集中讨论 Spring MVC 的一个重要注解 @RequestMapping
 
简要地说,该注解用于把 Web 请求映射到 Spring Controller 方法。
 
2. @RequestMapping 基础
 
先从一个简单的示例开始:通过设置基本条件把 HTTP 请求映射到某个方法。
 
2.1. 路径映射
 
@RequestMapping(value = “/ex/foos”, method = RequestMethod.GET)
@ResponseBody
public String getFoosBySimplePath(){
return“Get some Foos”;

}

 
使用 curl 命令测试:
curl -i http://localhost:8080/spring-rest/ex/foos
2.2. HTTP 方法映射
 
HTTP 方法参数没有缺省值,如果不设参数值,请求会映射到所有 HTTP 请求。
 
下面的示例中,映射的是 HTTP POST 请求:
 
@RequestMapping(value = “/ex/foos”, method = POST)
@ResponseBody
public String postFoos(){
return“Post some Foos”;
}
 
通过 curl 命令测试 POST 请求:
 
curl -i -X POST http://localhost:8080/spring-rest/ex/foos
 
3. 根据 HTTP Header 映射
 
3.1. 指定 Header 属性
 
通过为请求指定一个 header 进一步缩小映射:
 
@RequestMapping(value = “/ex/foos”, headers = “key=val”, method = GET)
@ResponseBody
public String getFoosWithHeader(){
return“Get some Foos with Header”;

}

 
使用 curl 命令带 header 测试:
 
curl -i -H “key:val” http://localhost:8080/spring-rest/ex/foos
 
还可以 @RequestMapping headers 属性设置多个 header:
 
@RequestMapping(
value = “/ex/foos”,
headers = { “key1=val1”, “key2=val2” }, method = GET)
@ResponseBody
public String getFoosWithHeaders(){
return“Get some Foos with Header”;
}
 
使用以下命令测试:
 
curl -i -H “key1:val1” -H “key2:val2” http://localhost:8080/spring-rest/ex/foos
 
注意:curl 分隔 key 和 value 的语法是冒号与 HTTP 规范相同,而 Spring 中使用的是等号。
 
3.2. consume 与 produce 属性
 
Controller 媒体类型映射时需要特别注意:使用 @RequestMapping headers 还可以处理 Accept header:
 
@RequestMapping(
value = “/ex/foos”,
method = GET,
headers = “Accept=application/json”)
@ResponseBody
public String getFoosAsJsonFromBrowser(){
return“Get some Foos with Header Old”;

}

 
定义 Accept Header 的匹配方式非常灵活。不仅支持 = 精确匹配,也支持多条件匹配,像下面这样:
 
curl -H “Accept:application/json,text/html”
http://localhost:8080/spring-rest/ex/foos
 
自 Spring 3.1 开始,@RequestMapping 注解已支持 produce 和 consume 属性,适用以下的场景:
 
@RequestMapping(
value = “/ex/foos”,
method = RequestMethod.GET,
produces = “application/json”
)
@ResponseBody
public String getFoosAsJsonFromREST(){
return“Get some Foos with Header New”;

}

 
同时,header 映射会自动转换为新的 produces 机制,执行的效果相同。
 
使用 curl 命令测试:
 
curl -H “Accept:application/json”
http://localhost:8080/spring-rest/ex/foos
 
此外,produces 也支持多个参数:
 
@RequestMapping(
value = “/ex/foos”,
method = GET,
produces = { “application/json”, “application/xml” }

)

 
请注意:Spring 不支持同时用新旧两种方法指定 accept header,否则会抛出异常:
 
Caused by: java.lang.IllegalStateException: Ambiguous mapping found.
Cannot map‘fooController’ bean method
java.lang.String
org.baeldung.spring.web.controller
.FooController.getFoosAsJsonFromREST()
to
{ [/ex/foos],
methods=[GET],params=[],headers=[],
consumes=[],produces=[application/json],custom=[]
}:
There is already ‘fooController’ bean method
java.lang.String
org.baeldung.spring.web.controller
.FooController.getFoosAsJsonFromBrowser()
mapped.
 
最后需要说明一点:produces consumes 与其它注解的行为不同,方法级注解只覆盖不增加
 
4. Path 变量
 
通过 @PathVariable 注解可以绑定 URI 变量。
 
4.1. @PathVariable 单个变量
 
单个变量使用示例:
 
@RequestMapping(value = “/ex/foos/{id}”, method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariable(
@PathVariable(“id”)long id) {
return“Get a specific Foo with id=” + id;

}

 
使用 curl 测试:
 
curl http://localhost:8080/spring-rest/ex/foos/1
 
如果方法的参数名与路径变量名完全匹配,可以直接使用@PathVariable 不带参数
 
@RequestMapping(value = “/ex/foos/{id}”, method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariable(
@PathVariable String id){
return“Get a specific Foo with id=” + id;
}
 
注意:@PathVariable 支持自动类型转换,id 还可以声明为:
 
@PathVariable long id
4.2. @PathVariable 多个变量
 
复杂 URI 可以把多个部分映射为多个 value:
@RequestMapping(value = “/ex/foos/{fooid}/bar/{barid}”, method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariables
(@PathVariable long fooid, @PathVariable long barid){
return“Get a specific Bar with id=” + barid +
” from a Foo with id=” + fooid;

}

同样可以使用 curl 测试:
 
curl http://localhost:8080/spring-rest/ex/foos/1/bar/2
 
4.3. @PathVariable 正则表达式
 
@PathVariable 支持正则表达式,比如用正则限制 id 只允许数值输入:
 
@RequestMapping(value = “/ex/bars/{numericId:[\d]+}”, method = GET)
@ResponseBody
public String getBarsBySimplePathWithPathVariable(
@PathVariable long numericId){
return“Get a specific Bar with id=” + numericId;
}
 
下面 URI 可以匹配:
 
http://localhost:8080/spring-rest/ex/bars/1
 
下面 URI 不能匹配:
 
http://localhost:8080/spring-rest/ex/bars/abc
 
5. Request Parameters
 
@RequestMapping 通过 @RequestParam 注解可以对 URL 参数进行映射
 
比如下面这个 URI:
 
http://localhost:8080/spring-rest/ex/bars?id=100
 
Java 代码:
@RequestMapping(value = “/ex/bars”, method = GET)
@ResponseBody
public String getBarBySimplePathWithRequestParam(
@RequestParam(“id”)long id) {
return“Get a specific Bar with id=” + id;

}

然后,为 Controller 方法加上 @RequestParam(“id”) 注解提取 id 参数值。
 
使用 curl 命令发送带 id 请求:
 
curl -i -d id=100 http://localhost:8080/spring-rest/ex/bars
 
在上面的例子中,参数直接绑定,没有提前声明。
 
@RequestMapping 可以根据需要有选择地定义参数 减少请求映射:
 
@RequestMapping(value = “/ex/bars”, params = “id”, method = GET)
@ResponseBody
public String getBarBySimplePathWithExplicitRequestParam(
@RequestParam(“id”)long id) {
return“Get a specific Bar with id=” + id;
}
 
还支持更灵活的映射,设置多个 params 值,不需要全部映射:
 
@RequestMapping(value = “/ex/bars”, params = “id”, method = GET)
@ResponseBody
public String getBarBySimplePathWithExplicitRequestParam(
@RequestParam(“id”)long id) {
return“Get a specific Bar with id=” + id;

}

 
还可以设置多个 params 值,只使用其中一部分:
 
@RequestMapping(
value = “/ex/bars”,
params = { “id”, “second” },
method = GET)
@ResponseBody
public String getBarBySimplePathWithExplicitRequestParams(
@RequestParam(“id”)long id) {
return“Narrow Get a specific Bar with id=” + id;
}
 
下面这个 URI:
 
http://localhost:8080/spring-rest/ex/bars?id=100&second=something
 
总能找到最佳的映射匹配(窄匹配),即同时定义了 idsecond
 
6. 边界案例
 
6.1. 多个 Path 映射到同一个 Controller 方法
 
虽然 @RequestMapping Path 通常与 Controller 方法一一对应,但这只是最佳实践而非硬性规定。某些情况需要把多个请求映射到同一个方法。这种情况下,@RequestMappingvalue 会包含多个映射
 
@RequestMapping(
value = { “/ex/advanced/bars”, “/ex/advanced/foos” },
method = GET)
@ResponseBody
public String getFoosOrBarsByPath(){
return“Advanced – Get some Foos or Bars”;

}

 
下面两个 curl 命令会命中相同的方法:
 
curl -i http://localhost:8080/spring-rest/ex/advanced/foos
curl -i http://localhost:8080/spring-rest/ex/advanced/bars
 
6.2. 多个 HTTP 请求映射到同一个 Controller 方法
 
可以把不同的 HTTP 请求映射到同一个 Controller 方法:
 
@RequestMapping(value = “*”, method = RequestMethod.GET)
@ResponseBody
public String getFallback(){
return“Fallback for GET Requests”;

}

 
甚至可以处理所有请求:
 
@RequestMapping(
value = “*”,
method = { RequestMethod.GET, RequestMethod.POST … })
@ResponseBody
public String allFallback(){
return“Fallback for All Requests”;
}
 
6.4. 映射模糊错误
 
对于不同的 Controller 方法,Spring 会根据 HTTP 方法、URL、参数、Header 和媒体类型计算请求映射,如果结果相同会报告模糊映射错误。下面是一个映射模糊案例:
 
@GetMapping(value = “foos/duplicate”)
public String duplicate(){
return“Duplicate”;
}
@GetMapping(value = “foos/duplicate”)
public String duplicateEx(){
return“Duplicate”;

}

 
抛出的异常包含以下错误信息:
 
Caused by: java.lang.IllegalStateException: Ambiguous mapping.
Cannot map‘fooMappingExamplesController’ method
public java.lang.String org.baeldung.web.controller.FooMappingExamplesController.duplicateEx()
to {[/ex/foos/duplicate],methods=[GET]}:
There is already ‘fooMappingExamplesController’ bean method
public java.lang.String org.baeldung.web.controller.FooMappingExamplesController.duplicate() mapped.
 
上面的报错信息可以看到,Spring 无法映射 org.baeldung.web.controller.FooMappingExamplesController.duplicateEx(),因为已经与 org.baeldung.web.controller.FooMappingExamplesController.duplicate() 发生了冲突。
 
下面这两个方法返回的内容类型不同,就不会发生映射模糊错误:
 
@GetMapping(value = “foos/duplicate”, produces = MediaType.APPLICATION_XML_VALUE)
public String duplicate(){
return“Duplicate”;
}
@GetMapping(value = “foos/duplicate”, produces = MediaType.APPLICATION_JSON_VALUE)
public String duplicateEx(){
return“Duplicate”;
}
 
要解决上面例子中的问题,最简单的方法修改其中一个 URL。
 
7. 快速映射
 
Spring Framework 4.3 加入了一些 HTTP 映射新注解:
 
  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping
 
提高了代码的可读性,代码得到精简。下同是一个数据库 CRUD 操作 RESTful API 示例:
 
@GetMapping(“/{id}”)
public ResponseEntity<?> getBazz(@PathVariable String id){
returnnew ResponseEntity<>(new Bazz(id, “Bazz”+id), HttpStatus.OK);
}
@PostMapping
public ResponseEntity<?> newBazz(@RequestParam(“name”) String name){
returnnew ResponseEntity<>(new Bazz(“5”, name), HttpStatus.OK);
}
@PutMapping(“/{id}”)
public ResponseEntity<?> updateBazz(
@PathVariable String id,
@RequestParam(“name”) String name) {
returnnew ResponseEntity<>(new Bazz(id, name), HttpStatus.OK);
}
@DeleteMapping(“/{id}”)
public ResponseEntity<?> deleteBazz(@PathVariable String id){
returnnew ResponseEntity<>(new Bazz(id), HttpStatus.OK);

}

 
8. Spring Configuration
 
Spring MVC 配置非常简单,FooController 定义如下:
 
package org.baeldung.spring.web.controller;
@Controller
publicclass FooController { … }
 
加上 @Configuration 就能启用 MVC并把 Controller 加到 classpath 搜索路径:
 
@Configuration
@EnableWebMvc
@ComponentScan({ “org.baeldung.spring.web.controller” })
publicclass MvcConfig {
//
}
 
9. 总结
 
本文详细讨论了 Spring @RequestMapping 注解,包括基础用例、HTTP header 映射、@PathVariable @RequestParam 示例。
 
想了解 Spring MVC 中另一个核心注解 @ModelAttribute
 
本文的完整代码可以在 Github 上找到。
github.com/eugenp/tutorials/tree/master/spring-rest-simple
本站所有文章均由网友分享,仅用于参考学习用,请勿直接转载,如有侵权,请联系网站客服删除相关文章。若由于商用引起版权纠纷,一切责任均由使用者承担
极客文库 » 一文了解 Spring RequestMapping

Leave a Reply

欢迎加入「极客文库」,成为原创作者从这里开始!

立即加入 了解更多