1. Información general
En este tutorial, nos centraremos en una de las principales anotaciones en Spring MVC: @RequestMapping.
En pocas palabras, la anotación se utiliza para asignar solicitudes web a los métodos de Spring Controller.
2. Conceptos básicos de @ RequestMapping
Comencemos con un ejemplo simple: mapear una solicitud HTTP a un método usando algunos criterios básicos.
2.1. @RequestMapping - por ruta
@RequestMapping(value = "/ex/foos", method = RequestMethod.GET) @ResponseBody public String getFoosBySimplePath() { return "Get some Foos"; }
Para probar este mapeo con un simple comando curl , ejecute:
curl -i //localhost:8080/spring-rest/ex/foos
2.2. @RequestMapping - el método HTTP
El parámetro del método HTTP no tiene ningún valor predeterminado. Entonces, si no especificamos un valor, se asignará a cualquier solicitud HTTP.
Aquí hay un ejemplo simple, similar al anterior, pero esta vez asignado a una solicitud HTTP POST:
@RequestMapping(value = "/ex/foos", method = POST) @ResponseBody public String postFoos() { return "Post some Foos"; }
Para probar la POST mediante un comando curl :
curl -i -X POST //localhost:8080/spring-rest/ex/foos
3. RequestMapping y encabezados HTTP
3.1. @RequestMapping con el atributo de encabezados
La asignación se puede reducir aún más especificando un encabezado para la solicitud:
@RequestMapping(value = "/ex/foos", headers = "key=val", method = GET) @ResponseBody public String getFoosWithHeader() { return "Get some Foos with Header"; }
Para probar la operación, usaremos el soporte de encabezado curl :
curl -i -H "key:val" //localhost:8080/spring-rest/ex/foos
e incluso varios encabezados a través del atributo de encabezados de @RequestMapping :
@RequestMapping( value = "/ex/foos", headers = { "key1=val1", "key2=val2" }, method = GET) @ResponseBody public String getFoosWithHeaders() { return "Get some Foos with Header"; }
Podemos probar esto con el comando:
curl -i -H "key1:val1" -H "key2:val2" //localhost:8080/spring-rest/ex/foos
Tenga en cuenta que para la sintaxis curl , dos puntos separan la clave del encabezado y el valor del encabezado, lo mismo que en la especificación HTTP, mientras que en Spring, se usa el signo igual.
3.2. @RequestMapping consume y produce
Merece especial atención el mapeo de los tipos de medios producidos por un método de controlador .
Podemos mapear una solicitud en función de su encabezado Accept a través del atributo de encabezados @RequestMapping introducido anteriormente:
@RequestMapping( value = "/ex/foos", method = GET, headers = "Accept=application/json") @ResponseBody public String getFoosAsJsonFromBrowser() { return "Get some Foos with Header Old"; }
La coincidencia para esta forma de definir el encabezado Accept es flexible: usa contiene en lugar de igual, por lo que una solicitud como la siguiente aún se asignaría correctamente:
curl -H "Accept:application/json,text/html" //localhost:8080/spring-rest/ex/foos
A partir de Spring 3.1, la anotación @RequestMapping ahora tiene los atributos produce y consume , específicamente para este propósito:
@RequestMapping( value = "/ex/foos", method = RequestMethod.GET, produces = "application/json" ) @ResponseBody public String getFoosAsJsonFromREST() { return "Get some Foos with Header New"; }
También, el viejo tipo de mapeo con la encabezados atributo será automáticamente convertida a la nueva produce mecanismo de arranque con Spring 3.1, por lo que los resultados serán idénticos.
Esto se consume vía rizo de la misma manera:
curl -H "Accept:application/json" //localhost:8080/spring-rest/ex/foos
Además, produce también admite varios valores:
@RequestMapping( value = "/ex/foos", method = GET, produces = { "application/json", "application/xml" } )
Tenga en cuenta que estas, las formas antiguas y nuevas de especificar el encabezado Accept , son básicamente la misma asignación, por lo que Spring no las permitirá juntas.
Tener ambos métodos activos resultaría en:
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.
Una nota final sobre el nuevo produce y consume mecanismos, que se comportan de manera diferente de la mayoría de otras anotaciones: Cuando se especifica en el nivel de tipo, las anotaciones a nivel de método no se complementan, pero anulan la información de nivel tipo.
Y, por supuesto, si desea profundizar en la creación de una API REST con Spring, consulte el nuevo curso REST con Spring .
4. RequestMapping con variables de ruta
Partes del URI de mapeo se pueden vincular a variables mediante la anotación @PathVariable .
4.1. Única @PathVariable
Un ejemplo simple con una sola variable de ruta:
@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 @PathVariableA 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 RegexRegular 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 Cases6.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.