Spring RequestMapping – 春天的RequestMapping

最后修改: 2013年 5月 16日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll focus on one of the main annotations in Spring MVC: @RequestMapping.

在本教程中,我们将重点讨论Spring MVC中的一个主要注释:@RequestMapping.

Simply put, the annotation is used to map web requests to Spring Controller methods.

简单地说,该注解用于将Web请求映射到Spring控制器方法。

2. @RequestMapping Basics

2.@RequestMapping 基础知识

Let’s start with a simple example: mapping an HTTP request to a method using some basic criteria.

让我们从一个简单的例子开始:使用一些基本标准将HTTP请求映射到一个方法。

2.1. @RequestMapping — by Path

2.1.@RequestMapping – by Path

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

To test out this mapping with a simple curl command, run:

要用一个简单的curl命令测试这个映射,请运行。

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

2.2. @RequestMapping — the HTTP Method

2.2.@RequestMapping – HTTP方法

The HTTP method parameter has no default. So, if we don’t specify a value, it’s going to map to any HTTP request.

HTTP method参数没有默认值。所以,如果我们不指定一个值,它就会映射到任何HTTP请求。

Here’s a simple example, similar to the previous one, but this time mapped to an HTTP POST request:

下面是一个简单的例子,与前面的例子类似,但这次是映射到一个HTTP POST请求。

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

To test the POST via a curl command:

要通过curl命令测试POST。

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

3. RequestMapping and HTTP Headers

3.RequestMapping和HTTP Headers

3.1. @RequestMapping With the headers Attribute

3.1.@RequestMappingheaders属性

The mapping can be narrowed even further by specifying a header for the request:

通过为请求指定一个头,可以进一步缩小映射的范围。

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

To test the operation, we’re going to use the curl header support:

为了测试该操作,我们将使用curl头支持。

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

and even multiple headers via the headers attribute of @RequestMapping:

甚至可以通过@RequestMappingheaders属性实现多个头。

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

We can test this with the command:

我们可以用命令来测试这一点。

curl -i -H "key1:val1" -H "key2:val2" http://localhost:8080/spring-rest/ex/foos

Note that for the curl syntax, a colon separates the header key and the header value, the same as in the HTTP spec, while in Spring, the equals sign is used.

请注意,对于curl语法,用冒号来分隔头的键和头的值,这与HTTP规范中的相同,而在Spring中,使用的是等号。

3.2. @RequestMapping Consumes and Produces

3.2.@RequestMapping 消耗和产生

Mapping media types produced by a controller method is worth special attention.

对控制器产生的媒体类型方法的映射值得特别注意。

We can map a request based on its Accept header via the @RequestMapping headers attribute introduced above:

我们可以通过上面介绍的@RequestMapping headers属性,根据一个请求的Accept头进行映射。

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

The matching for this way of defining the Accept header is flexible — it uses contains instead of equals, so a request such as the following would still map correctly:

这种定义Accept头的方式的匹配很灵活–它使用包含而不是等价,所以像下面这样的请求仍然可以正确映射。

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

Starting with Spring 3.1, the @RequestMapping annotation now has the produces and consumes attributes, specifically for this purpose:

从Spring 3.1开始,@RequestMapping注解现在有producesconsumes属性,专门用于此目的。

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

Also, the old type of mapping with the headers attribute will automatically be converted to the new produces mechanism starting with Spring 3.1, so the results will be identical.

另外,从Spring 3.1开始,带有headers属性的旧类型映射将自动转换为新的produces机制,所以结果将是相同的。

This is consumed via curl in the same way:

这是通过curl以同样的方式消耗的。

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

Additionally, produces supports multiple values as well:

此外,produces也支持多个值。

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

Keep in mind that these — the old and new ways of specifying the Accept header — are basically the same mapping, so Spring won’t allow them together.

请记住,这些–指定Accept头的新旧方式–基本上是相同的映射,所以Spring不允许它们在一起。

Having both these methods active would result in:

如果这两种方法都激活,将导致。

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.

A final note on the new produces and consumes mechanisms, which behave differently from most other annotations: When specified at the type level, the method-level annotations do not complement but override the type-level information.

关于新的producesconsumes机制的最后说明,其行为与大多数其他注释不同。当在类型级指定时,方法级注解不是补充而是覆盖类型级信息。

And of course, if you want to dig deeper into building a REST API with Spring, check out the new REST with Spring course.

当然,如果你想更深入地了解用Spring构建REST API,检查一下新的REST with Spring课程

4. RequestMapping With Path Variables

4.使用路径变量的RequestMapping

Parts of the mapping URI can be bound to variables via the @PathVariable annotation.

映射URI的一部分可以通过@PathVariable注解绑定到变量。

4.1. Single @PathVariable

4.1.单个@PathVariable

A simple example with a single path variable:

一个简单的例子,只有一个路径变量。

@RequestMapping(value = "/ex/foos/{id}", method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariable(
  @PathVariable("id") long id) {
    return "Get a specific Foo with id=" + id;
}

This can be tested with curl:

这可以用curl来测试。

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

If the name of the method parameter matches the name of the path variable exactly, then this can be simplified by using @PathVariable with no value:

如果方法参数的名称与路径变量的名称完全吻合,那么可以通过使用无值的@PathVariable来简化。

@RequestMapping(value = "/ex/foos/{id}", method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariable(
  @PathVariable String id) {
    return "Get a specific Foo with id=" + id;
}

Note that @PathVariable benefits from automatic type conversion, so we could have also declared the id as:

请注意,@PathVariable受益于自动类型转换,所以我们也可以将id声明为。

@PathVariable long id

4.2. Multiple @PathVariable

4.2.多个@PathVariable

A more complex URI may need to map multiple parts of the URI to multiple values:

一个更复杂的URI可能需要将URI的多个部分映射到多个值

@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;
}

This is easily tested with a curl in the same way:

这很容易用同样的方法用curl来测试。

curl http://localhost:8080/spring-rest/ex/foos/1/bar/2

4.3. @PathVariable With Regex

4.3.@PathVariable与Regex

Regular expressions can also be used when mapping the @PathVariable.

在映射@PathVariable.时也可以使用正则表达式。

For example, we will restrict the mapping to only accept numerical values for the id:

例如,我们将限制映射只接受id的数字值。

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

This will mean that the following URIs will match:

这将意味着以下URI将匹配。

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

But this will not:

但这不会。

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

5. RequestMapping With Request Parameters

5.RequestMapping与请求参数

@RequestMapping allows easy mapping of URL parameters with the @RequestParam annotation.

@RequestMapping允许轻松地@RequestParam注解对URL参数进行映射

We are now mapping a request to a URI:

我们现在正在将一个请求映射到一个URI。

http://localhost:8080/spring-rest/ex/bars?id=100
@RequestMapping(value = "/ex/bars", method = GET)
@ResponseBody
public String getBarBySimplePathWithRequestParam(
  @RequestParam("id") long id) {
    return "Get a specific Bar with id=" + id;
}

We are then extracting the value of the id parameter using the @RequestParam(“id”) annotation in the controller method signature.

然后我们使用控制器方法签名中的@RequestParam(“id”)注解来提取id参数的值。

To send a request with the id parameter, we’ll use the parameter support in curl:

为了发送一个带有id参数的请求,我们将使用curl中的参数支持。

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

In this example, the parameter was bound directly without having been declared first.

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

For more advanced scenarios, @RequestMapping can optionally define the parameters as yet another way of narrowing the request mapping:

对于更高级的情况,@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;
}

Even more flexible mappings are allowed. Multiple params values can be set, and not all of them have to be used:

甚至允许更灵活的映射。可以设置多个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;
}

And of course, a request to a URI such as:

当然,对一个URI的请求,如:。

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

will always be mapped to the best match — which is the narrower match, which defines both the id and the second parameter.

将总是被映射到最佳匹配–这是较窄的匹配,它同时定义了idsecond参数。

6. RequestMapping Corner Cases

6.RequestMapping角落案例

6.1. @RequestMapping — Multiple Paths Mapped to the Same Controller Method

6.1.@RequestMapping – 多个路径映射到同一个控制器方法

Although a single @RequestMapping path value is usually used for a single controller method (just good practice, not a hard and fast rule), there are some cases where mapping multiple requests to the same method may be necessary.

尽管单个@RequestMapping路径值通常用于单个控制器方法(只是良好的实践,不是硬性规定),但在某些情况下,可能需要将多个请求映射到同一个方法。

In that case, the value attribute of @RequestMapping does accept multiple mappings, not just a single one:

在这种情况下,@RequestMappingvalue属性确实接受多个映射,而不是只有一个。

@RequestMapping(
  value = { "/ex/advanced/bars", "/ex/advanced/foos" }, 
  method = GET)
@ResponseBody
public String getFoosOrBarsByPath() {
    return "Advanced - Get some Foos or Bars";
}

Now both of these curl commands should hit the same method:

现在这两个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

6.2.@RequestMapping – 多个HTTP请求方法到同一个控制器方法

Multiple requests using different HTTP verbs can be mapped to the same controller method:

使用不同HTTP动词的多个请求可以被映射到同一个控制器方法。

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

With curl, both of these will now hit the same 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

6.3.@RequestMapping – 所有请求的回退

To implement a simple fallback for all requests using a particular HTTP method, for example, for a GET:

为所有使用特定HTTP方法的请求实现一个简单的回退,例如,为GET。

@RequestMapping(value = "*", method = RequestMethod.GET)
@ResponseBody
public String getFallback() {
    return "Fallback for GET Requests";
}

or even for all requests:

甚至是所有的请求。

@RequestMapping(
  value = "*", 
  method = { RequestMethod.GET, RequestMethod.POST ... })
@ResponseBody
public String allFallback() {
    return "Fallback for All Requests";
}

6.4. Ambiguous Mapping Error

6.4.模糊的映射错误

The ambiguous mapping error occurs when Spring evaluates two or more request mappings to be the same for different controller methods. A request mapping is the same when it has the same HTTP method, URL, parameters, headers, and media type.

当Spring评估两个或更多的请求映射对不同的控制器方法是相同的时候,就会出现模糊的映射错误。当一个请求映射具有相同的HTTP方法、URL、参数、头信息和媒体类型时,它就是相同的。

For example, this is an ambiguous mapping:

例如,这是一个模棱两可的映射。

@GetMapping(value = "foos/duplicate" )
public String duplicate() {
    return "Duplicate";
}

@GetMapping(value = "foos/duplicate" )
public String duplicateEx() {
    return "Duplicate";
}

The exception thrown usually does have error messages along these lines:

抛出的异常通常确实有类似的错误信息。

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.

A careful reading of the error message points to the fact that Spring is unable to map the method org.baeldung.web.controller.FooMappingExamplesController.duplicateEx(), as it has a conflicting mapping with an already mapped org.baeldung.web.controller.FooMappingExamplesController.duplicate().

仔细阅读错误信息,发现Spring无法映射方法org.baeldung.web.controller.FooMappingExamplesController.duplicateEx(),因为它与已经映射的org.baeldung.web.controller.FooMappingExamplesController.duplicate()有冲突的映射。

The code snippet below will not result in ambiguous mapping error because both methods return different content types:

下面的代码片段不会导致模棱两可的映射错误,因为两种方法都返回不同的内容类型

@GetMapping(value = "foos/duplicate", produces = MediaType.APPLICATION_XML_VALUE)
public String duplicateXml() {
    return "<message>Duplicate</message>";
}
    
@GetMapping(value = "foos/duplicate", produces = MediaType.APPLICATION_JSON_VALUE)
public String duplicateJson() {
    return "{\"message\":\"Duplicate\"}";
}

This differentiation allows our controller to return the correct data representation based on the Accepts header supplied in the request.

这种区分允许我们的控制器根据请求中提供的Accepts头返回正确的数据表示。

Another way to resolve this is to update the URL assigned to either of the two methods involved.

解决这个问题的另一个方法是更新分配给两个相关方法中任何一个的URL。

7. New Request Mapping Shortcuts

7.新的请求映射捷径

Spring Framework 4.3 introduced a few new HTTP mapping annotations, all based on @RequestMapping:

Spring Framework 4.3引入了一些新的HTTP映射注释,全部基于@RequestMapping

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping

These new annotations can improve the readability and reduce the verbosity of the code.

这些新的注释可以提高代码的可读性,减少代码的冗长性。

Let’s look at these new annotations in action by creating a RESTful API that supports CRUD operations:

让我们通过创建一个支持CRUD操作的RESTful API,来看看这些新注解的作用:

@GetMapping("/{id}")
public ResponseEntity<?> getBazz(@PathVariable String id){
    return new ResponseEntity<>(new Bazz(id, "Bazz"+id), HttpStatus.OK);
}

@PostMapping
public ResponseEntity<?> newBazz(@RequestParam("name") String name){
    return new ResponseEntity<>(new Bazz("5", name), HttpStatus.OK);
}

@PutMapping("/{id}")
public ResponseEntity<?> updateBazz(
  @PathVariable String id,
  @RequestParam("name") String name) {
    return new ResponseEntity<>(new Bazz(id, name), HttpStatus.OK);
}

@DeleteMapping("/{id}")
public ResponseEntity<?> deleteBazz(@PathVariable String id){
    return new ResponseEntity<>(new Bazz(id), HttpStatus.OK);
}

A deep dive into these can be found here.

对这些的深入研究可以在这里找到。

8. Spring Configuration

8.弹簧配置

The Spring MVC Configuration is simple enough, considering that our FooController is defined in the following package:

考虑到我们的FooController被定义在下面的包中,Spring MVC配置足够简单。

package org.baeldung.spring.web.controller;

@Controller
public class FooController { ... }

We simply need a @Configuration class to enable the full MVC support and configure classpath scanning for the controller:

我们只需要一个@Configuration类来启用完整的MVC支持和配置控制器的classpath扫描。

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

9. Conclusion

9.结论

This article focused on the @RequestMapping annotation in Spring, discussing a simple use case, the mapping of HTTP headers, binding parts of the URI with @PathVariable, and working with URI parameters and the @RequestParam annotation.

本文重点介绍了Spring中的@RequestMapping注解,讨论了一个简单的用例、HTTP头的映射、用@PathVariable绑定URI的部分内容,以及与URI参数和@RequestParam注解的工作。

If you’d like to learn how to use another core annotation in Spring MVC, you can explore the @ModelAttribute annotation here.

如果您想了解如何在Spring MVC中使用另一个核心注解,您可以在这里探索@ModelAttribute注解

The full code from the article is available over on GitHub.

文章中的完整代码可在GitHub上获得over