Anotaciones de primavera: @RequestMapping y sus variantes

Si leyó algo sobre Spring, desarrolló un proyecto o estaba remotamente interesado en cómo funciona, le presentaron @RequestMapping annot ...

Introducción

Si leyó algo sobre Primavera, desarrolló un proyecto o estaba remotamente interesado en cómo funciona, se le presentó la anotación @RequestMapping.

Es una de las anotaciones básicas en Spring que asigna solicitudes HTTP (URL) con métodos:

1
2
3
4
@RequestMapping("/")
public void helloWorld() {
    return "Hello World!";
}

Nos permite ejecutar métodos y fragmentos de código cada vez que el usuario final accede a un punto final con una solicitud HTTP. En este caso, es un mapeo raíz simple que devuelve la cadena "¡Hola mundo!" cuando se alcanza el extremo raíz.

La anotación @RequestMapping

La anotación @RequestMapping ofrece más de lo que se muestra en el ejemplo anterior. Vamos a cubrir brevemente algunos de los casos de uso básicos de la anotación.

@RequestMapping Scopes

El mapeo se puede asignar a un nivel de clase o a un nivel de método:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@RequestMapping("/admin")
public class AdminController() {
    @RequestMapping("/adminPanel")
    public String adminPanel() {
        return "adminPanel.html";
    }

    @RequestMapping("/statistics")
    public String statistics() {
        return "statistics.html";
    }
}

Aquí, la asignación /admin se asigna a nivel de clase, mientras que las asignaciones /adminPanel y /statistics se asignan a los métodos. La asignación de nivel de clase se utilizará como prefijo para todas estas asignaciones de nivel de método: /admin/adminPanel y /admin/statistics.

De esta manera, podemos segregar nuestra aplicación en partes lógicas. Por ejemplo, toda la lógica relacionada con el administrador estará detrás del muro /admin, al que solo pueden acceder los usuarios administradores. Todos los usuarios que no sean administradores serán redirigidos al lado del cliente de la aplicación.

Múltiples Mapeos por Método

Si es necesario, podemos asignar varias asignaciones a un solo método, como:

1
2
3
4
@RequestMapping({"/", "/index", "/home"})
public void helloWorld() {
    return "Hello World!";
}

Alcanzar cualquiera de estos puntos finales dará como resultado que el método helloWorld() maneje nuestra solicitud.

Métodos de solicitud

El atributo más común utilizado es el atributo método que nos permite especificar y restringir el método de solicitud que maneja nuestro método (GET, POST, DELETE, etc.):

1
2
3
4
@RequestMapping(value = "/addProduct", method = RequestMethod.POST)
public String addProductPost(@ModelAttribute("product") Product product) {
    // some code
}

Si no especificamos un método de solicitud, el método de solicitud predeterminado es GET.

Encabezados de solicitud

Podemos especificar aún más el mapeo definiendo uno o más encabezados. En este caso, el método helloWorld() manejará la solicitud si su tipo de contenido es text/plain o text/html:

1
2
3
4
@RequestMapping(value = "/header", headers = {"content-type=text/plain", "content-type=text/html"})
String helloWorld() {
    return "Hello World!";
}

Variables de ruta

Muchos sitios web se basan en variables de ruta para mostrar un producto, página, perfil, etc. específico al usuario final, como example.com/viewProduct/324, donde 324 es el ID del producto:

1
2
3
4
5
6
7
@RequestMapping("/viewProduct/{productId}")
public String viewProduct(@PathVariable int productId, Model model) {
    Product product = productService.getProductById(productId);
    model.addAttribute("product", product);

    return "viewProduct";
}

Aquí, después del mapeo, encerramos la variable de ruta entre corchetes ({}) y asignamos ese valor a nuestro productId. La anotación @PathVariable se encarga de esto por nosotros. Luego, podemos encontrar el producto a través de nuestro ProductService y mostrárselo al usuario final.

Por supuesto, el mismo enfoque se puede utilizar para otras variables como:

1
2
3
4
5
@RequestMapping("/users/{username}")
public String viewUser(@PathVariable("username") String username) {
    Profile profile = profileService.getProfileByUsername(username);
    // rest of the code
}

Parámetros de solicitud

Muy similar a las variables de ruta, muchas aplicaciones se basan en parámetros de solicitud para alterar el estado de la página. Podemos usar la anotación @RequestParam para vincular un parámetro de solicitud a una variable:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@RequestMapping("/login")
public String login(@RequestParam(value = "error", required = false) String error, @RequestParam(value = "logout", required = false) String logout, Model model) {

    if (error != null) {
        model.addAttribute("error", "Wrong username or password!");
    }
    if (logout != null) {
        model.addAttribute("msg", "You have successfully logged out!");
    }
    return "login";
}

Producible y consumible

Usando la anotación @RequestMapping, también podemos producir o consumir tipos de medios. Para producir un tipo de medio como "JSON", puede usar el atributo produce junto con la anotación @ResponseBody, mientras que para consumir un tipo de medio puede usar el atributo consumes:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@RequestMapping("/annotations")
public class SomeController {
    @RequestMapping(value = "/produce", produces = {"application/json"})
    @ResponseBody
    String produces() {
        return "Here's how you use the 'produces' attribute";
    }

    @RequestMapping(value = "/consume", consumes = {"application/JSON", "application/XML"})
    String consume() {
        return "Here's how you use a 'consume' attribute";
    }
}

Variantes @RequestMapping compuestas

Un solo mapeo puede corresponder a diferentes métodos si nos aseguramos de que no haya ambigüedad en cuanto a qué método debe manejar qué versión del mapeo.

Podemos tener múltiples métodos con el mismo mapeo si su método de solicitud es diferente:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@RequestMapping("/hello")
public String hello() {
    return "hello";
}

@RequestMapping(value = "/hello", method = RequestMethod.POST)
public String helloPost() {
    return "hello post";
}

@RequestMapping(value = "/hello", method = RequestMethod.PUT)
public String helloGet() {
    return "hello put";
}

@RequestMapping(value = "/hello", method = RequestMethod.DELETE)
public String helloDelete() {
    return "hello delete";
}

Este mismo método tiene 4 variaciones diferentes:

  • Predeterminado (OBTENER)
  • CORREO
  • PONER
  • ELIMINAR

Con más variaciones, la legibilidad del código se reduce y, sinceramente, esto ya es detallado para un mapeo simple. Las asignaciones de solicitudes en aplicaciones más grandes pueden volverse muy complejas y enrevesadas, y nos gustaría mantenerlas más simples y legibles.

Para este propósito, se nos presentan varias anotaciones nuevas, las variantes @RequestMapping:

  • @GetMapping
  • @PostMapeo
  • @DeleteMapping
  • @PutMapping
  • @PatchMapping

Literalmente, solo hay versiones de acceso directo para las asignaciones anteriores, donde @PostMapping("/hello") es igual a @RequestMapping(value="/hello", RequestMethod.POST).

Echando un vistazo más de cerca a cómo se definen estas anotaciones, podemos tener una idea clara de lo que hacen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {

    @AliasFor(annotation = RequestMapping.class)
    String name() default "";

    @AliasFor(annotation = RequestMapping.class)
    String[] value() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] path() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] params() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] headers() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] produces() default {};
}

Dicho esto, podemos reescribir los ejemplos anteriores:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@GetMapping
public String hello() {
    return "hello";
}

@PostMapping
public String helloPost() {
    return "hello post";
}

@PutMapping
public String helloGet() {
    return "hello put";
}

@DeleteMapping
public String helloDelete() {
    return "hello delete";
}

Como esta es una forma más simple, más legible y más nueva de escribir anotaciones, generalmente se prefiere escribirlas así. Si es necesario, aún puede definir cualquier otro atributo, como valor, parámetros, ruta, etc.

Conclusión

@RequestMapping es una anotación por excelencia en el marco Spring que nos permite mapear solicitudes HTTP con métodos que desearíamos ejecutar.

A partir de Spring 4.3, se prefiere y se recomienda usar los accesos directos creados para esta anotación para obtener una base de código más legible y menos detallada.