Yêu cầu mùa xuân

1. Khái quát chung

Trong hướng dẫn này, chúng ta sẽ tập trung vào một trong những chú thích chính trong Spring MVC: @RequestMapping.

Nói một cách đơn giản, chú thích được sử dụng để ánh xạ các yêu cầu web tới các phương thức Spring Controller.

2. Kiến thức cơ bản về @ RequestMapping

Hãy bắt đầu với một ví dụ đơn giản: ánh xạ một yêu cầu HTTP tới một phương thức sử dụng một số tiêu chí cơ bản.

2.1. @RequestMapping - theo Path

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

Để kiểm tra ánh xạ này bằng một lệnh curl đơn giản , hãy chạy:

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

2.2. @RequestMapping - Phương thức HTTP

Tham số phương thức HTTP không có mặc định. Vì vậy, nếu chúng tôi không chỉ định một giá trị, nó sẽ ánh xạ tới bất kỳ yêu cầu HTTP nào.

Đây là một ví dụ đơn giản, tương tự như trước đó, nhưng lần này được ánh xạ tới một yêu cầu HTTP POST:

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

Để kiểm tra POST thông qua lệnh curl :

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

3. RequestMapping và HTTP Headers

3.1. @RequestMapping Với thuộc tính headers

Ánh xạ có thể được thu hẹp hơn nữa bằng cách chỉ định tiêu đề cho yêu cầu:

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

Để kiểm tra hoạt động, chúng tôi sẽ sử dụng hỗ trợ tiêu đề curl :

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

và thậm chí nhiều tiêu đề thông qua thuộc tính tiêu đề của @RequestMapping :

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

Chúng ta có thể kiểm tra điều này bằng lệnh:

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

Lưu ý rằng đối với cú pháp curl , dấu hai chấm phân tách khóa tiêu đề và giá trị tiêu đề, giống như trong thông số kỹ thuật HTTP, trong khi trong Spring, dấu bằng được sử dụng.

3.2. @RequestMapping Tiêu thụ và Sản xuất

Ánh xạ các loại phương tiện được tạo ra bằng phương pháp bộ điều khiển đáng được chú ý đặc biệt.

Chúng tôi có thể ánh xạ một yêu cầu dựa trên tiêu đề Chấp nhận của nó thông qua thuộc tính tiêu đề @RequestMapping được giới thiệu ở trên:

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

Đối sánh cho cách xác định tiêu đề Chấp nhận này rất linh hoạt - nó sử dụng hàm chứa thay vì bằng, do đó, một yêu cầu như sau sẽ vẫn ánh xạ chính xác:

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

Bắt đầu với Spring 3.1, chú thích @RequestMapping hiện có các thuộc tính sản xuấttiêu thụ , cụ thể cho mục đích này:

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

Ngoài ra, kiểu ánh xạ cũ với thuộc tính headers sẽ tự động được chuyển đổi sang cơ chế tạo mới bắt đầu với Spring 3.1, vì vậy kết quả sẽ giống hệt nhau.

Điều này được tiêu thụ thông qua cuộn tròn theo cùng một cách:

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

Ngoài ra, sản xuất cũng hỗ trợ nhiều giá trị:

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

Hãy nhớ rằng những cách này - những cách cũ và mới để chỉ định tiêu đề Chấp nhận - về cơ bản là cùng một ánh xạ, vì vậy Spring sẽ không cho phép chúng kết hợp với nhau.

Có cả hai phương thức này hoạt động sẽ dẫn đến:

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.

Lưu ý cuối cùng về cơ chế sản xuấttiêu thụ mới , hoạt động khác với hầu hết các chú thích khác: Khi được chỉ định ở cấp loại, chú thích cấp phương pháp không bổ sung mà ghi đè thông tin cấp loại.

Và tất nhiên, nếu bạn muốn tìm hiểu sâu hơn về cách xây dựng API REST với Spring, hãy xem khóa học REST mới với Spring .

4. RequestMapping với các biến đường dẫn

Các phần của URI ánh xạ có thể được liên kết với các biến thông qua chú thích @PathVariable .

4.1. @PathVariable đơn

Một ví dụ đơn giản với một biến đường dẫn:

@RequestMapping(value = "/ex/foos/{id}", method = GET) @ResponseBody public String getFoosBySimplePathWithPathVariable( @PathVariable("id") long id) { return "Get a specific Foo with/ex/foos/{id}", method = GET) @ResponseBody public String getFoosBySimplePathWithPathVariable( @PathVariable String id) { return "Get a specific Foo with2-multiple-pathvariable">4.2. Multiple @PathVariable

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

@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 from a Foo with3-pathvariable-with-regex">4.3. @PathVariable With Regex

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

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

@RequestMapping(value = "/ex/bars/{numericId:[\\d]+}", method = GET) @ResponseBody public String getBarsBySimplePathWithPathVariable( @PathVariable long numericId) { return "Get a specific Bar withrequest-param">5. RequestMapping With Request Parameters

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

We are now mapping a request to a URI:

//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/ex/bars", params = "id", method = GET) @ResponseBody public String getBarBySimplePathWithExplicitRequestParam( @RequestParam("id") long id) { return "Get a specific Bar with/ex/bars", params = { "id", "second" }, method = GET) @ResponseBody public String getBarBySimplePathWithExplicitRequestParams( @RequestParam("id") long id) { return "Narrow Get a specific Bar withcorner-cases">6. RequestMapping Corner Cases

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

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.

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

@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 -i //localhost:8080/spring-rest/ex/advanced/foos curl -i //localhost:8080/spring-rest/ex/advanced/bars

6.2. @RequestMapping — Multiple HTTP Request Methods to the Same Controller Method

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

@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 -i -X POST //localhost:8080/spring-rest/ex/foos/multiple curl -i -X PUT //localhost:8080/spring-rest/ex/foos/multiple

6.3. @RequestMapping — a Fallback for All Requests

To implement a simple fallback for all requests using a particular HTTP method, for example, for a 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

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.

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().

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 "Duplicate"; } @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.

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

7. New Request Mapping Shortcuts

Spring Framework 4.3 introduced a few new HTTP mapping annotations, all based on @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:

@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

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

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 @EnableWebMvc @ComponentScan({ "org.baeldung.spring.web.controller" }) public class MvcConfig { // }

9. Conclusion

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.

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

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