Skip to content

Ejemplo de RestController con Spring Boot y Swagger

Un ejemplo básico de RestController en una aplicación Spring Boot usando @GetMapping y @PostMapping, con documentación de API generada automáticamente vía Swagger.

En este artículo, explicaré cómo configurar un ejemplo básico de RestController en una aplicación Spring Boot, usando tanto las anotaciones @GetMapping como @PostMapping. Además, esta aplicación incluye Springfox Swagger para que los endpoints de la REST API estén documentados y sean accesibles a través de esta herramienta.

Anotaciones de Controller de Spring

Muchas personas conocen las ventajas de usar Spring para crear una REST API de una manera muy eficiente. Desde la versión 4 (4.3), Spring tiene algunas anotaciones que hacen aún más fácil escribir Controllers habilitados para REST:

  • @RestController: Es una anotación de conveniencia que combina @Controller y @ResponseBody. Esta última, cuando se establece a nivel de clase, hace que todos los métodos anotados con @RequestMapping sean considerados como anotados con @ResponseBody también. Y, en caso de que no lo sepas, necesitas la anotación @ResponseBody cuando retornas datos desde los métodos de tu controller para construir una interfaz REST apropiada (como puedes adivinar, para retornar datos como JSON en el cuerpo de tu respuesta).
  • @GetMapping y @PostMapping son anotaciones de acceso directo para @RequestMapping(method = RequestMethod.GET) y @RequestMapping(method = RequestMethod.POST). Cuando se combinan con el @RestController mencionado arriba, los métodos anotados serán tratados como si estuvieran anotados con @ResponseBody.

¡Vaya! ¿Demasiadas anotaciones para digerir? Llegaremos a una explicación más práctica sobre esto más adelante al echar un vistazo a la clase Controller.

Código

Repositorio GitHub: Todo el código incluido en este post está disponible en GitHub: Spring Boot RestController. Si lo encuentras útil, ¡por favor dale una estrella!

¡Es hora de ver el código! Comencemos con el pom.xml para que veas la configuración y dependencias incluidas:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>es.macero.dev</groupId>
    <artifactId>spring-boot-restcontroller-example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>spring-boot-restcontroller-example</name>
    <description>Example project using RestController with Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.6.1</version>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.6.1</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Las líneas resaltadas son las dependencias necesarias para incluir Springfox Swagger en nuestro proyecto. Veremos las ventajas de esto pronto. Ahora, para tener algo de código con qué jugar, he creado una clase SpanishGreeting con solo un mensaje en ella:

package es.macero.dev.restexample;

class SpanishGreeting {

    private String message;

    // Required for JSON deserialization
    SpanishGreeting() {
    }

    public SpanishGreeting(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

Esa es una clase muy simple. La única parte extraña es ese constructor vacío allí. Si estás familiarizado con trabajar con JSON sabrás que este constructor es necesario para el deserializador de Jackson para transformar un mensaje JSON entrante a la clase Java correspondiente (en nuestro caso al llamar al método POST abajo). Usualmente, no necesitas este constructor para nada más, así que no debería ser público.

Y, finalmente, la parte principal de nuestra aplicación: la clase controller.

@RestController
@RequestMapping("/spanish-greetings")
public class SpanishGreetingController {

    private List<SpanishGreeting> spanishGreetings;

    public SpanishGreetingController() {
        spanishGreetings = new ArrayList<>();
        spanishGreetings.add(new SpanishGreeting("Hola!"));
        spanishGreetings.add(new SpanishGreeting("Qué tal?!"));
        spanishGreetings.add(new SpanishGreeting("Buenas!"));
    }

    @GetMapping("/{id}")
    public SpanishGreeting getSpanishGreetingById(@PathVariable("id") final int id) {
        return spanishGreetings.get(id - 1); // list index starts with 0 but we prefer to start on 1
    }

    @GetMapping("/random")
    public SpanishGreeting getRandom() {
        return spanishGreetings.get(new Random().nextInt(spanishGreetings.size()));
    }

    @PostMapping
    @ResponseStatus(HttpStatus.OK)
    public void createSpanishGreeting(@RequestBody SpanishGreeting spanishGreeting) {
        spanishGreetings.add(spanishGreeting);
    }
}

Veamos qué tenemos aquí:

  • La clase está anotada con @RestController. Como se explicó arriba, si usamos solo @Controller en su lugar, entonces necesitamos algunas anotaciones extra @ResponseBody a nivel de método para getSpanishGreetingById y getRandom. O, desde la versión 4 de Spring, también podríamos anotar la clase con @ResponseBody y tener el mismo efecto. Así que, la anotación @RestController es solo un acceso directo de dos anotaciones.
  • La clase también está anotada con @RequestMapping, esa es una forma conveniente para establecer la misma url de contexto para todos los métodos definidos dentro de la clase. En nuestro caso, los endpoints resultantes serán:
    • GET /spanish-greetings/{id}
    • GET /spanish-greetings/random
    • POST /spanish-greetings
  • Los métodos anotados con @GetMapping son igual que si estuvieran anotados con @RequestMapping(method = RequestMethod.GET). De nuevo, no necesitamos @ResponseBody en estos métodos. La anotación @PathVariable indica a Spring de dónde tomar ese valor de variable, en este caso del path. Podríamos usar también @RequestParam si fuera un parámetro en su lugar (también cambiando el patrón de mapeo).
  • @PostMapping, como puedes adivinar, es en realidad lo mismo que @RequestMapping(method = RequestMethod.POST). No usamos aquí ningún valor de mapeo extra porque queremos postear nuestros datos al mapeo de la colección, siguiendo las mejores convenciones de REST API.
  • El @ResponseStatus(HttpStatus.OK) no es necesario aquí, solo está allí para ver explícitamente que queremos retornar ese código de estado, pero en realidad es el predeterminado.

Y ahora algunas curiosidades:

  • Como puede que hayas imaginado, hay algunas otras anotaciones de acceso directo para DELETE, PUT, etc. Revisa este enlace si tienes curiosidad.
  • En Spring, si no usas el método para un @RequestMapping, estás aceptando todos ellos. Eso significa que si por ejemplo cambias el @GetMapping("/random") por @RequestMapping("/random"), puedes hacer un DELETE /spanish-greetings/random y aún recibirás un bonito saludo en español. Siempre especifica el método cuando definas tu API con Spring o fácilmente caerás en malas prácticas. Puedes probar esto en el código y verificar los cambios en la UI de Swagger (verás muchos más endpoints).
  • Como muchas otras características inteligentes de Spring MVC, es capaz de convertir nuestra variable de path id a int mágicamente, usando el convertidor de tipo por defecto.
  • Puede que te estés preguntando por qué la anotación @ResponseBody fue mágicamente embebida dentro de @RestController pero aún necesitamos escribir la anotación @RequestBody para nuestro método POST. ¿Por qué no hacer lo mismo? Puedes encontrar un buen razonamiento en este ticket JIRA para el proyecto Spring. Resumen: porque sería ir demasiado lejos con suposiciones, dependiendo de la librería de cliente que uses podría haber algunos parámetros de consulta útiles.

Probando la API con Swagger UI

Gracias a las dependencias agregadas al proyecto y esta configuración agregada a nuestra aplicación Spring Boot,

@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
            .select()
            .apis(RequestHandlerSelectors.any())
            .paths(PathSelectors.regex("/spanish-greeting.*"))
            .build();
}

podemos navegar a http://localhost:8080/swagger-ui.html (después de iniciar nuestra aplicación) y jugar con la REST API:

Swagger UI screenshot
Swagger UI para la REST API de Spring Boot

En este caso, estamos usando Swagger de la manera más básica, y principalmente para usar la UI, pero es una herramienta poderosa para agregar documentación a tu REST API solo agregando anotaciones a tu código.

Puedes probar la API de las siguientes formas:

  • Haz algunos GETs a /random, deberías ver los Saludos en Español predefinidos siendo recuperados.
  • GET algunos de ellos por id, los predeterminados son 1, 2, 3.
  • POST un nuevo saludo, será incluido en la lista y puedes accederlo por id o aleatoriamente. ¿Quieres sugerencias? Solo intenta “Buenos días!” o “Buenas tardes!”…

Aprende Más: Si estás interesado en Spring Boot y crear microservicios reales (no de una clase) con APIs Restful e integración entre ellos, consigue mi libro hoy y obtén experiencia real en Arquitectura de Microservicios.

Master Microservices with Spring Boot

A hands-on guide to building production-ready microservices

Get the Book

Get More Architecture Insights

Join hundreds of software architects and engineers getting practical insights delivered to their inbox.

Volver al Blog