Demande de printemps

1. Vue d'ensemble

Dans ce didacticiel, nous allons nous concentrer sur l'une des principales annotations de Spring MVC: @RequestMapping.

En termes simples, l'annotation est utilisée pour mapper les requêtes Web aux méthodes Spring Controller.

2. Principes de base de RequestMapping

Commençons par un exemple simple: mapper une requête HTTP à une méthode en utilisant certains critères de base.

2.1. @RequestMapping - par chemin

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

Pour tester ce mappage avec une simple commande curl , exécutez:

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

2.2. @RequestMapping - la méthode HTTP

Le paramètre de méthode HTTP n'a pas de valeur par défaut. Donc, si nous ne spécifions pas de valeur, il sera mappé à n'importe quelle requête HTTP.

Voici un exemple simple, similaire au précédent, mais cette fois mappé à une requête HTTP POST:

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

Pour tester le POST via une commande curl :

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

3. RequestMapping et en-têtes HTTP

3.1. @RequestMapping avec l' attribut headers

Le mappage peut être encore réduit en spécifiant un en-tête pour la requête:

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

Pour tester l'opération, nous allons utiliser le support d'en-tête curl :

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

et même plusieurs en-têtes via l' attribut headers de @RequestMapping :

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

Nous pouvons tester cela avec la commande:

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

Notez que pour la syntaxe curl , un signe deux-points sépare la clé d'en-tête et la valeur d'en-tête, comme dans la spécification HTTP, tandis que dans Spring, le signe égal est utilisé.

3.2. @RequestMapping consomme et produit

Le mappage des types de supports produits par une méthode de contrôleur mérite une attention particulière.

Nous pouvons mapper une requête en fonction de son en- tête Accept via l' attribut d'en- têtes @RequestMapping introduit ci-dessus:

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

La correspondance pour cette façon de définir l'en- tête Accept est flexible - elle utilise contient au lieu d'égal, donc une demande telle que la suivante serait toujours mappée correctement:

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

À partir de Spring 3.1, l' annotation @RequestMapping a maintenant les attributs produit et consomme , spécifiquement à cette fin:

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

De plus, l'ancien type de mappage avec l' attribut headers sera automatiquement converti vers le nouveau mécanisme de production à partir de Spring 3.1, de sorte que les résultats seront identiques.

Ceci est consommé via curl de la même manière:

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

De plus, produit prend également en charge plusieurs valeurs:

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

Gardez à l'esprit que ceux-ci - l'ancienne et la nouvelle façon de spécifier l'en- tête Accept - sont fondamentalement le même mappage, donc Spring ne les autorisera pas ensemble.

Avoir ces deux méthodes actives entraînerait:

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.

Une note finale sur le nouveau produit et consomme des mécanismes, qui se comportent différemment de la plupart des autres annotations: lorsqu'elles sont spécifiées au niveau du type, les annotations au niveau de la méthode ne complètent pas mais remplacent les informations au niveau du type.

Et bien sûr, si vous souhaitez approfondir la création d'une API REST avec Spring, consultez le nouveau cours REST avec Spring .

4. RequestMapping avec des variables de chemin

Des parties de l'URI de mappage peuvent être liées à des variables via l' annotation @PathVariable .

4.1. Simple @PathVariable

Un exemple simple avec une seule variable de chemin:

@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.