Documenta tus microservicios SpringBoot con SpringFox

A la hora de diseñar un microservicio que va a ser consumido por otras aplicaciones, ya sean Front, otros Backends o incluso aplicaciones móviles, lo más habitual hoy en día es usar un API REST para realizar el intercambio de datos, usando JSON como formato.

Este sistema facilita mucho la integración entre sistemas, pero no debemos olvidarnos que detrás de un sistema existe una persona encargada de realizar dicha integración. Para facilitar la integración con un sistema que “habla” REST, existen diversos estándares: Swagger, RAML o JSONAPI, que proveen de una interfaz “human readable” para que los desarrolladores puedan implementar de forma más fácil y eficaz dichas integraciones.

En este post vamos a ver cómo documentar el API REST usando Java como lenguaje de programación, Spring (con su módulo SpringBoot para exponer el API) y SpringFox, una librería que ofrece tanto el documento de especificación Swagger como una interfaz web para entender (incluso probar) el API REST.

En primer lugar, iremos a la página de Spring Initializer para generar el esqueleto de nuestro microservicio con SpringBoot:

Seleccionamos la dependencia Web y haremos clic en Generate project para descargar el esqueleto, que importaremos como proyecto Maven en el IDE que usemos.

Una vez importado el proyecto, la estructura que tendremos será la siguiente:

He renombrado el fichero de propiedades a aplication.yml. Además, hemos reutilizado el controller, bean, service y repository, podéis ver cómo se hace en este post, y consultar y descargar las clases de este repositorio

NOTA: La implementación del repository la he modificado para “mockear” los métodos y así no incorporar la dependencia de Mongo.

Una vez incorporados estos componentes, la estructura del proyecto sería:

A modo de recordatorio, el servicio expone dos operaciones REST:

  • GET /api/person/{id}: para consultar una persona por su identificador.
  • POST /api/person: para insertar una nueva persona.

El objetivo es contar con una documentación del API REST que cumpla la especificación 2.0 de Swagger, y además con una interfaz web amigable que nos permita entender y probar dicho API REST.

Para incluir SpringFox, lo primero que haremos será incluir las dependencias de SpringFox y del UI en nuestro pom de Maven:

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

Después, editaremos la clase de arranque del SpringBoot para incluir los siguientes elementos:

  1. La anotación @EnableSwagger2 en la cabecera de la clase:
    @SpringBootApplication
    @EnableSwagger2
    public class SpringbootSpringfoxApplication {
    
  2. La inyección por dependencia de la clase TypeResolver de fasterxml:
    @Autowired
    private TypeResolver typeResolver;
    
  3. La inyección por valor de la propiedad app.version definida en el fichero application.yml con el valor de la versión establecida en el pom:
    – Fichero application.yml:

    app:
       version: '@pom.version@'
    

    Inyección:

    @Value("${app.version}")
    private String appVersion;
    
  4. El método anotado con @Bean que devuelve un objeto Docket y que configura el API con Swagger:
    @Bean
    public Docket swaggerApi() {
    return new Docket(DocumentationType.SWAGGER_2)
    	.select()
    		.apis(RequestHandlerSelectors
    			.basePackage("com.isolisduran.springboot.controller"))
    		.paths(PathSelectors.regex("/api/.*"))
    		.build()
    	.pathMapping("/")
    	.genericModelSubstitutes(ResponseEntity.class)
    	.alternateTypeRules(AlternateTypeRules.newRule(
    		typeResolver.resolve(DeferredResult.class,
    		typeResolver.resolve(ResponseEntity.class,WildcardType.class)), 
    		typeResolver.resolve(WildcardType.class)))
    	.useDefaultResponseMessages(false)
    	.apiInfo(apiInfo());
    }
    

    Aunque la configuración de Swagger con SpringFox permite muchas más opciones, hemos dejado las más básicas para entender su funcionamiento. Lo más relevante de este método es:

    · Establece qué paquete del proyecto contiene los controllers que se expondrán en el API:

    basePackage("com.isolisduran.springboot.controller")
    

    · A su vez, solo expone los controllers que expongan el path indicado:

    PathSelectors.regex("/api/.*")
    
  5. El método apiInfo() para documentar los datos básicos del API:
    private ApiInfo apiInfo() {
    	return new ApiInfo(
    		"API Title",
    		"API Description",
    		appVersion, 
    		"urn:tos", 
    		new Contact("Contact Name", "http://www.none.com", "test@test.com"), 
    		"API License",
    		"http://www.api-license-url.com");
    }
    

    Con estos simples cambios ya tendríamos una documentación básica de nuestro API REST. Para probarlo, compilamos con:

    $ mvn clean package
    

    Y ejecutamos el jar resultante con:

    $ java -jar target/springboot-springfox-0.0.1-SNAPSHOT.jar
    

    Con este comando arrancaremos el servidor en el puerto 8080 en nuestro entorno local para poder probar SpringFox. Abrimos un navegador y accedemos a la url local http://localhost:8080/v2/api-docs. Veremos el JSON de Swagger:

A su vez, si accedemos a http://localhost:8080/swagger-ui.html veremos la interfaz web del API REST:

Con esto tendríamos documentada de forma básica nuestro API REST, pero SpringFox ofrece más opciones para documentar nuestro API:

Documentación de Controllers:

SpringFox permite documentar los Controllers de Spring usando anotaciones:

  1. En el método GET añadiremos lo marcado en negrita:
    @ApiOperation(value="Get a person by id", notes="Provides an operation to get a Person object by its identifier")
    @ApiResponses(value={
    @ApiResponse(code=200, message="OK", response=Person.class),
    @ApiResponse(code=404, message="Not Found", response=String.class),
    @ApiResponse(code=500, message="Internal Server Error", response=String.class)
    })
    @RequestMapping(method = RequestMethod.GET, value = "/person/{tiged}", produces = MediaType.APPLICATION_JSON_VALUE)
    public @ResponseBody ResponseEntity<Person> getById(@ApiParam(value="Person identifier", required=true) @PathVariable int id) {
    	Person person = personService.getById(id);
    	return new ResponseEntity<>(person, HttpStatus.OK);
    }
    
  2. En el método POST añadiremos igualmente lo marcado en negrita:
    @ApiOperation(value="Create a new Person", notes="Provides an operation to create a new Person object and return its identifier")
    @ApiResponses(value={
    @ApiResponse(code=201, message="Created", response=String.class),
    @ApiResponse(code=400, message="Bad Request", response=String.class),
    @ApiResponse(code=500, message="Internal Server Error", response=String.class)
    })
    @RequestMapping(method = RequestMethod.POST, value = "/person", produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
    public @ResponseBody ResponseEntity<String> create(@ApiParam(value="JSON with Person to create", required=true) @Valid @RequestBody Person person) {
    	String newPersonId = String.valueOf(personService.create(person).getId());
    	return new ResponseEntity<String>(newPersonId, HttpStatus.CREATED);
    }
    

NOTA: Las anotaciones anteriores son específicas de Swagger y son provistas por la dependencia que SpringFox tiene de Swagger.

Documentación de Beans:

El la clase Person incluiremos las siguientes anotaciones:

@ApiModelProperty(value="Person identifier", required=true, example="1", position=1)
private int id;	
@ApiModelProperty(value="Person name", required=true, example="MyName", position=2)
private String name;	
@ApiModelProperty(value="Person address", required=false, example="MyName", position=3)
private String address;

NOTA: Al igual que en el caso del controller, las anotaciones anteriores son específicas de Swagger y son provistas por la dependencia que SpringFox tiene de Swagger.

Una vez hemos incluido estas anotaciones, compilamos de nuevo el proyecto con mvn clean package, ejecutamos de nuevo con java -jar para arrancar y probar el servicio en nuestro entorno local, y recargamos la url local http://localhost:8080/swagger-ui.html en nuestro navegador:

Documentación del Get:

Documentación del Post:

Como podemos observar, con las anotaciones incluidas, tenemos una documentación más rica, con descripciones de los atributos de métodos y objetos, explicación de cada operación y resultados esperados en caso de éxito y error.

Además, como podemos observar, podemos probar interactivamente las operaciones expuestas en el API:

Ejecución del Get:

Ejecución del Post:

SpringFox nos proporciona de forma muy amigable la posibilidad de documentar nuestra API REST sin tener que generar la documentación nosotros mismos a mano.

A la vez que escribimos el código podemos documentar también el API, con lo que siempre tendremos control sobre los cambios introducidos. Además, al incluir el UI, facilita también la integración y pruebas por parte de las aplicaciones consumidoras.

Podéis encontrar el código de ejemplo en el repositorio de GitHub de Paradigma

Con más de 10 años como Software Developer en Java, desde J2EE tradicional hasta servicios Cloud-Ready, le encanta "cacharrear" con todo lo tecnológico tanto en casa como en el trabajo. Además, es un apasionado de la fotografía y "runner" en sus ratos libres. Huye de los "dinosaurios tecnológicos" y busca equipos altamente motivados. Actualmente es Arquitecto de Software en Paradigma.

Ver toda la actividad de Isidro Solís

Escribe un comentario