Spring RequestMapping

1. 概述

在这篇文章中,我们将讨论Spring MVC中主要的注解-@RequestMapping-通常用作web请求和Spring Controller方法的映射。

整篇文章中,我们将通过简单的curl命令来测试每一个映射。

2、RequestMapping 基础

让我们开始一个简单的例子吧-用一个简单的方法将一个HTTP请求映射到一个方法。

2.1. @RequestMapping – by Path

@RequestMapping(value = "/ex/foos")
@ResponseBody
public String getFoosBySimplePath() {
    return "Get some Foos";
}

用简单的curl命令测试这个映射,运行:

curl -i http://localhost:8080/spring-rest/ex/foos

2.2. @RequestMapping – the HTTP Method

默认情况下,@RequestMapping映射的方法是GET-这当然能改变了-例如映射到一个POST请求:

@RequestMapping(value = "/ex/foos", method = RequestMethod.POST)
@ResponseBody
public String postFoos() {
    return "Post some Foos";
}

curl命令来测试POST请求:

curl -i -X POST http://localhost:8080/spring-rest/ex/foos

3. RequestMapping and HTTP Headers

3.1. @RequestMapping with the headers attribute


进一步通过指定请求头来减小映射范:

@RequestMapping(value = "/ex/foos", headers = "key=val")
@ResponseBody
public String getFoosWithHeader() {
    return "Get some Foos with Header";
}

@RequestMapping的header属性可以指定多个头:

@RequestMapping(value = "/ex/foos", headers = { "key1=val1", "key2=val2" })
@ResponseBody
public String getFoosWithHeaders() {
    return "Get some Foos with Header";
}

我们用curl header support来测试这个操作

curl -i -H "key:val" http://localhost:8080/spring-rest/ex/foos

注意curl分离键和值的语法使用一个分号,和HTTP规则一样,而在Spring中使用的是等号。

3.2. @RequestMapping Consumes and Produces


映射生成的媒体类型到一个Controller方法是特别值得关注的-我们能基于Accept头来映射请求通过上面介绍的@RequestMapping的Header属性:

@RequestMapping(value = "/ex/foos", method = GET, headers = "Accept=application/json")
@ResponseBody
public String getFoosAsJsonFromBrowser() {
    return "Get some Foos with Header Old";
}

定义Accept头的这种匹配方式是灵活的-它只要包含就行,并不必须得相等,因此像下面的请求仍然可以正确的映射:

curl -H "Accept:application/json,text/html" http://localhost:8080/spring-rest/ex/foos

Spring3.1开始,@RequestMapping注解有produces和consumes属性,就能达到这个目的:

@RequestMapping(value = "/ex/foos", method = RequestMethod.GET, produces = "application/json")
@ResponseBody
public String getFoosAsJsonFromREST() {
    return "Get some Foos with Header New";
}

至Spring3.1起,用header属性的这种旧方式将自动的转换为新的produces机制,因此结果是一样的。

这是以同样的方式来使用curl:

curl -H "Accept:application/json" http://localhost:8080/spring-rest/ex/foos

另外,produces属性也支持多个值:

@RequestMapping(value = "/ex/foos", produces = { "application/json", "application/xml" })

牢记这些-指定accept头的新旧方式-都是基于相同的映射,因此Spring不能同时定义它们两个-如果出现两个结果如下:

Caused by: java.lang.IllegalStateException: Ambiguous mapping found.
Cannot map 'fooController' bean method
public 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
public java.lang.String org.baeldung.spring.web.controller.FooController.getFoosAsJsonFromBrowser()
mapped.

关于produces和consumes机制的最后一点是-与大部分的其它注解是不同的,当定义在类上面时,方法上的注解不会补充而是重写类上的信息。

4. RequestMapping with Path Variables

通过@PathVariable注解映射URI的部分能绑定一个变量。

4.1. Single @PathVariable

使用单个变量的一个简单的示例:

@RequestMapping(value = "/ex/foos/{id}")
@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}")
@ResponseBody
public String getFoosBySimplePathWithPathVariable(@PathVariable String id) {
   return "Get a specific Foo with id=" + id;
}

注意:@PathVariable能自动类型转换,因此我们能这样声明id:

@PathVariable long id

4.2. Multiple @PathVariable

更复杂的URI需要URI的多个部分映射到多个值:

@RequestMapping(value = "/ex/foos/{fooid}/bar/{barid}")
@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 with RegEx

当用@PathVariable做映射时可以用正则表达式;例如:我们限制映射对于id的值只接收数值类型的:

@RequestMapping(value = "/ex/bars/{numericId:[\d]+}")
@ResponseBody
public String getBarsBySimplePathWithPathVariable(@PathVariable final long numericId) {
    return "Get a specific Bar with id=" + numericId;
}

这就意味着下面的URI匹配:

http://localhost:8080/spring-rest/ex/bars/1

这个就不行:

http://localhost:8080/spring-rest/ex/bars/abc

5. RequestMapping with Request Parameters

@RequestMapping用@RequestParam注解可以很容易得映射到URL参数。

例如,我们现在将请求映射到URI:

http://localhost:8080/spring-rest/ex/bars?id=100

@RequestMapping(value = "/ex/bars")
@ResponseBody
public String getBarBySimplePathWithRequestParam(@RequestParam("id") long id) {
    return "Get a specific Bar with id=" + id;
}

我们用在controller方法签名中的@RequestParam(“id”)注解获取到参数id的值。

发送一个带参数id的请求,我们将用在curl中的参数支持:

curl -i -d id=100 http://localhost:8080/spring-rest/ex/bars

在这个例子中,参数并没有声明而是直接被绑定。

对于更高级的方案,@RequestMapping可以准确的定义参数映射为另一个缩小请求映射的方式:

@RequestMapping(value = "/ex/bars", params = "id")
@ResponseBody
public String getBarBySimplePathWithExplicitRequestParam(@RequestParam("id") long id) {
    return "Get a specific Bar with id=" + id;
}

更灵活的映射也是被允许的-可以定义多个参数值,而且这些参数值不用都用到:

@RequestMapping(value = "/ex/bars", params = { "id", "second" })
@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

总是会被映射到最佳的匹配-就是范围小的匹配,也就是定义了id和second参数的匹配。

6. RequestMapping Corner Cases

6.1. @RequestMapping – multiple paths mapped to the same controller method


尽管一个@RequestMapping路径值通常对应一个controller方法,这是一个好的习惯,但不是一个严格的规定-对于一些案列将多个请求映射到同一个方法是必要的。对于这样的案列,@RequestMapping的value属性接受多个映射,不仅仅是一个:

@RequestMapping(value = { "/ex/advanced/bars", "/ex/advanced/foos" })
@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. @RequestMapping – multiple HTTP request methods to the same controller method

用不同HTTP请求动作的多个请求可以映射到相同的controller方法上。

@RequestMapping(value = "/ex/foos/multiple", method = { RequestMethod.PUT, RequestMethod.POST })
@ResponseBody
public String putAndPostFoos() {
    return "Advanced - PUT and POST within single method";
}

用curl,这两个将访问同一个方法:

curl -i -X POST http://localhost:8080/spring-rest/ex/foos/multiple
curl -i -X PUT http://localhost:8080/spring-rest/ex/foos/multiple

6.3. @RequestMapping – a fallback for all requests

用一个特定的HTTP方法为所有的请求来实现一个简单的处理:

@RequestMapping(value = "*")
@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";
}


7. Spring Configuration


Spring MVC的配置是非常简单的-考虑到我们的FooController被定义在下面的包中:

package org.baeldung.spring.web.controller;
 
@Controller
public class FooController { ... }

至Spring3.1开始,我们仅仅需要一个@Configuration类来支撑整个MVC和配置类路径来扫描这个controller

@Configuration
@EnableWebMvc
@ComponentScan({ "org.baeldung.spring.web.controller" })
public class MvcConfig {
    //
}

8. Conclusion


这篇文章专注于Spring中的@RequestMapping注解-讨论一下简单的使用,HTTP头的映射,用@PathVariable绑定部分的URI和处理URI参数和@RequestParam注解。

你可能感兴趣的:(Spring RequestMapping)