Spring RequestMapping

Spring RequestMapping

1. Vue d'ensemble

Dans cet article, nous allons nous concentrer sur l'une des principales annotations deSpring MVC – @RequestMapping.

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

Lectures complémentaires:

Servir des ressources statiques avec Spring

Comment mapper et gérer des ressources statiques avec Spring MVC - utilisez la configuration simple, puis la version 3.1 plus flexible et enfin les nouveaux résolveurs de ressources 4.1.

Read more

Mise en route des formulaires dans Spring MVC

Apprenez à travailler avec des formulaires à l'aide de Spring MVC - mappage d'une entité de base, soumission, affichage des erreurs.

Read more

Convertisseurs de messages HTTP avec la structure Spring

Comment configurer HttpMessageConverters pour une API REST avec Spring et comment utiliser ces convertisseurs avec RestTemplate.

Read more

2. @RequestMapping Principes de base

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 commandecurl, exécutez:

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

2.2. @RequestMapping - la méthode HTTP

Le paramètre HTTPmethod ano default - 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 commandecurl:

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

3. RequestMapping et en-têtes HTTP

3.1. @RequestMapping avec l'attributheaders

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

@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 la prise en charge de l'en-têtecurl:

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

Et même plusieurs en-têtes via l'attributheader 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" http://localhost:8080/spring-rest/ex/foos

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

3.2. @RequestMapping consomme et produit

Le mappage de la méthodemedia types produced by a controller mérite une attention particulière - nous pouvons mapper une requête en fonction de son en-têteAccept via l'attribut@RequestMapping headers 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êteAccept est flexible - elle utilise contient au lieu d'égal, donc une requête telle que celle-ci serait toujours mappée correctement:

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

À partir de Spring 3.1, les@RequestMapping annotation now has the produces and the consumes attributes, spécifiquement à cet effet:

@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'attributheaders sera automatiquement converti vers le nouveau mécanismeproduces à partir de Spring 3.1, les résultats seront donc identiques.

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

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

De plus,produces 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êteaccept - sont fondamentalement le même mappage, donc Spring ne les permettra 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.example.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.example.spring.web.controller
  .FooController.getFoosAsJsonFromBrowser()
mapped.

Une dernière remarque sur le nouveau mécanismeproduces etconsumes - ceux-ci se comportent différemment de la plupart des autres annotations: lorsqu'ils sont spécifiés au niveau du type,the method level annotations do not complement but overrideest l'information au niveau du type.

Et bien sûr, si vous souhaitez approfondir la création d'une API REST avec Spring -check outthe new REST with Spring course.

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

Un exemple simple avec une variable de chemin unique:

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

Cela peut être testé aveccurl:

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

Si le nom du paramètre de méthode correspond exactement au nom de la variable de chemin, cela peut être simplifié parusing @PathVariable with no value:

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

Notez que@PathVariable bénéficie de la conversion de type automatique, nous aurions donc pu également déclarer l'id comme:

@PathVariable long id

4.2. Plusieurs@PathVariable

Un URI plus complexe peut avoir besoin de mapper plusieurs parties de l'URI à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 id=" + barid +
      " from a Foo with id=" + fooid;
}

Ceci est facilement testé avec uncurl de la même manière:

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

4.3. @PathVariable avec RegEx

Les expressions régulières peuvent également être utilisées lors du mappage des@PathVariable; par exemple, nous limiterons le mappage pour n'accepter que des valeurs numériques pour lesid:

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

Cela signifie que les URI suivants correspondront:

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

Mais cela ne va pas:

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

5. RequestMapping avec des paramètres de requête

@RequestMapping permet desmapping of URL parameters with the @RequestParam annotation faciles. __

Nous mappons maintenant une demande à un URI tel que:

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

Nous extrayons ensuite la valeur du paramètreid en utilisant l'annotation@RequestParam(“id”) dans la signature de la méthode du contrôleur.

Pour envoyer une requête avec le paramètreid, nous utiliserons la prise en charge des paramètres danscurl:

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

Dans cet exemple, le paramètre a été lié directement sans avoir été déclaré auparavant.

Pour des scénarios plus avancés,@RequestMapping can optionally define the parameters - comme un autre moyen de restreindre le mappage des demandes:

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

Des mappages encore plus flexibles sont autorisés - plusieurs valeursparams peuvent être définies, et elles ne doivent pas toutes être utilisées:

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

Et bien sûr, une demande à un URI tel que:

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

Sera toujours mappé à la meilleure correspondance - qui est la correspondance la plus étroite, qui définit à la fois le paramètreid et le paramètresecond.

6. Cas d'angleRequestMapping

6.1. @RequestMapping - Plusieurs chemins mappés à la même méthode de contrôleur

Bien qu'une seule valeur de chemin@RequestMapping soit généralement utilisée pour une méthode de contrôleur unique, il ne s'agit que d'une bonne pratique et non d'une règle stricte et rapide - dans certains cas, le mappage de plusieurs requêtes vers la même méthode peut être nécessaire. Dans ce cas,the value attribute of @RequestMapping does accept multiple mappings, pas seulement un seul:

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

Maintenant, ces deux commandes curl devraient utiliser la même méthode:

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

6.2. @RequestMapping - Plusieurs méthodes de requête HTTP vers la même méthode de contrôleur

Plusieurs demandes utilisant différents verbes HTTP peuvent être mappées à la même méthode de contrôleur:

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

Aveccurl, les deux utiliseront désormais la même méthode:

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 - Une solution de secours pour toutes les demandes

Pour implémenter une solution de secours simple pour toutes les demandes utilisant une méthode HTTP particulière - par exemple, pour un GET:

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

Ou même pour toutes les demandes:

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

6.4. Erreur de mappage ambiguë

L'erreur de mappage ambigu se produit lorsque Spring évalue que deux mappages de demande ou plus sont identiques pour différentes méthodes de contrôleur. Un mappage de demande est identique lorsqu'il a la même méthode HTTP, URL, paramètres, en-têtes et type de média. Ceci, par exemple, est un mappage ambigu:

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

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

L'exception levée a généralement des messages d'erreur dans les lignes suivantes:

Caused by: java.lang.IllegalStateException: Ambiguous mapping.
  Cannot map 'fooMappingExamplesController' method
  public java.lang.String org.example.web.controller.FooMappingExamplesController.duplicateEx()
  to {[/ex/foos/duplicate],methods=[GET]}:
  There is already 'fooMappingExamplesController' bean method
  public java.lang.String org.example.web.controller.FooMappingExamplesController.duplicate() mapped.

Une lecture attentive du message d'erreur indique que Spring est incapable de mapper la méthodeorg.example.web.controller.FooMappingExamplesController.duplicateEx()  car il a un mappage en conflit avec unorg.example.web.controller.FooMappingExamplesController.duplicate(). déjà mappé

L'extrait de code ci-dessous ne générera pas d'erreur de mappage ambigu, car les deux méthodes renvoient des types de contenu différents:

@GetMapping(value = "foos/duplicate/xml", produces = MediaType.APPLICATION_XML_VALUE)
public String duplicateXml() {
    return "Duplicate";
}

@GetMapping(value = "foos/duplicate/json", produces = MediaType.APPLICATION_JSON_VALUE)
public String duplicateJson() {
    return "Duplicate";
}

Le moyen évident de résoudre ce problème consiste à mettre à jour l'URL attribuée à l'une des deux méthodes impliquées.

7. Nouveaux raccourcis de mappage de demande

Spring Framework 4.3 a introduit les annotations de mappage HTTPa few new, toutes basées sur@RequestMapping:

  • @GetMapping

  • @PostMapping

  • @PutMapping

  • @DeleteMapping

  • @PatchMapping

Ces nouvelles annotations peuvent améliorer la lisibilité et réduire la verbosité du code. Voyons ces nouvelles annotations en action en créant une API RESTful prenant en charge les opérations CRUD:

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

Une plongée approfondie dans ces derniers peut être trouvéehere.

8. Configuration du ressort

La configuration Spring MVC est assez simple - étant donné que notreFooController est défini dans le package suivant:

package org.example.spring.web.controller;

@Controller
public class FooController { ... }

Nous avons simplement besoin d'une classe@Configuration pour activer la prise en charge complète de MVC et configurer l'analyse du chemin de classe pour le contrôleur:

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

9. Conclusion

Cet article se concentre sur les@RequestMapping annotation in Spring - discutant d'un cas d'utilisation simple, le mappage des en-têtes HTTP, la liaison de parties de l'URI avec@PathVariable et l'utilisation des paramètres URI et l'annotation@RequestParam.

Si vous souhaitez apprendre à utiliser une autre annotation principale dans Spring MVC, vous pouvez explorerthe @ModelAttribute annotation here.

Le code complet de l'article est disponibleon GitHub. Ceci est un projet Maven, il devrait donc être facile à importer et à exécuter tel quel.