Quien más y quien menos en este mundo del software, alguna vez ha tenido que escribir una biblioteca de clases (mal llamada librería) o una API REST. O, al menos, ha tenido que modificarla. Y como profesionales que somos, siempre documentamos y explicamos el funcionamiento para que los consumidores puedan usarla fácilmente ¿verdad? 😜
Aunque me gustaría que la respuesta fuese que siempre se documenta bien, basta con que piense en mis propios proyectos pasados para saber que no es así. ¿Y qué nos aporta esa documentación? Pues si estamos hablando de una biblioteca o un paquete NuGet (que es una biblioteca, al fin y al cabo), la diferencia salta a simple vista:
La diferencia es abismal, ¿no? En el primer ejemplo, que está sin documentar, somos como un mamut con pistolas pegando tiros a ver qué sale. En cambio, en el segundo caso, tenemos una información que escribió el propio desarrollador del método, donde nos explica qué hace, qué hacen sus parámetros y qué valores de retorno o excepciones pueden existir (si las hubiese).
Para conseguir esta maravilla, simplemente es necesario que en el propio código de la clase introduzcamos unas anotaciones especiales llamadas sumarios, donde vamos a añadir esos mensajes que queremos que se vean desde fuera, por ejemplo:
/// <summary>
/// This is the awesome class that contains the awesome method
/// </summary>
public class MyAwesomeAPI
{
/// <summary>
/// This method is awesome and solves your problems
/// </summary>
/// <param name="parameter1">This is the first parameter for the awesome method</param>
/// <param name="parameter2">This is the second parameter for the awesome method</param>
public void MyAwesomeMethod(int parameter1, int parameter2)
{
}
}
Esto es sencillo al estar perfectamente integrado, incluirse en el código y formar parte del propio binario, pero... ¿podemos seguir este planteamiento en una API REST hecha en C#?
OpenAPI al rescate
Pues evidentemente, si estas leyendo esto es porque hay una solución y esa solución se llama OpenAPI. Pero, ¿qué es OpenAPI? Si nos vamos a la definición de su propia web (traducida por mí):
La Especificación OpenAPI (OAS) define una descripción de interfaz estándar y de lenguaje de programación para las API de REST, que permite tanto a los humanos como a las computadoras descubrir y comprender las capacidades de un servicio sin necesidad de acceder al código fuente, a documentación adicional o a la inspección del tráfico de la red. Cuando se define adecuadamente a través de OpenAPI, un consumidor puede comprender e interactuar con el servicio remoto con una cantidad mínima de lógica de implementación. De manera similar a lo que las descripciones de interfaz han hecho para la programación de nivel inferior, la especificación OpenAPI elimina las conjeturas al llamar a un servicio. -- OpenAPI Specification
Y esto, traducido a palabras mortales, quiere decir que la propia API REST va a exponer de manera adicional uno o más endpoints que se pueden consultar para descubrir qué hay en esa API. Lo mínimo que va a ofrecer, es un fichero json
en el que se define el funcionamiento de la API REST (endpoints disponibles, verbos, autenticaciones...). El formato de este json
está estandarizado y eso permite a aplicaciones y personas entender qué expone una API REST sin tener que hacer "mágica chamánica".
Un ejemplo del json
que genera el archiconocido WeatherForecast
(la API REST de ejemplo de ASP.NET):
{
"openapi": "3.0.1",
"info": {
"title": "PostSwagger",
"version": "v1"
},
"paths": {
"/WeatherForecast": {
"get": {
"tags": [
"WeatherForecast"
],
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/WeatherForecast"
}
}
},
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/WeatherForecast"
}
}
},
"text/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/WeatherForecast"
}
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"WeatherForecast": {
"type": "object",
"properties": {
"date": {
"type": "string",
"format": "date-time"
},
"temperatureC": {
"type": "integer",
"format": "int32"
},
"temperatureF": {
"type": "integer",
"format": "int32",
"readOnly": true
},
"summary": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
}
}
}
}
Aunque sin conocer el formato cuesta un poco saber qué está exponiendo la API REST que nos ofrece el json
, más o menos se deja entrever que hay un endpoint en /WeatherForecast
, cuyo único retorno definido es un HTTP 200 con un array de tipo WeatherForecast
. Este array contiene 4 propiedades llamadas date
, temperatureC
, temperatureF
y summary
.
Aunque con devolver este json
sería suficiente, a las personas nos suelen gustar más las tablas, los botones, las etiquetas... O sea, todo lo visual y que nos dé menos trabajo... Así que, lo bueno de que el formato sea estándar, es que existen herramientas que nos ofrecen una interfaz gráfica sobre este json
, de modo que interactuar se vuelve mucho más sencillo.
Incluso podemos probar desde el navegador los diferentes endpoint que ofrece:
Swagger en .Net 5
¿Y cómo podemos tener esta maravilla en nuestras APIs REST?
Si el proyecto que estamos creando es ASP .NET Core en .Net 5, basta con que marquemos el selector Habilitar soporte para OpenAPI
:
Al hacerlo, se crea automáticamente un proyecto con todo lo necesario para que tanto el fichero swagger.json
, como el endpoint de la interfaz gráfica (/swagger/index.html
) estén disponibles.
¡Estupendo!
Swagger en .Net Core
¿Y qué podemos hacer si el proyecto no es .Net 5?
Pues, las versiones anteriores no tienen soporte durante la creación del proyecto, pero añadirlo es algo muy sencillo.
Lo primero que vamos a necesitar, es añadir a nuestro proyecto el paquete NuGet Swashbuckle.AspNetCore
.
Después, en el método ConfigureServices
de la clase StartUp
el código, tenemos que añadir esto:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "NombreDeMiApi", Version = "v1" });
});
Y por último, vamos a registrar el middleware que se va a encargar de servir la interfaz de usuario y el fichero json
añadiendo en el método Configure
:
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json",
"NombreDeMiApi v1"));
Por tener una visión completa:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "NombreDeMiApi", Version = "v1" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json",
"NombreDeMiApi v1"));
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
¡Listo! Con esto conseguimos lo mismo que .Net 5 nos proporciona de serie.
Conclusión
Con solo hacer esto, ya hemos conseguido una configuración básica de nuestra documentación OpenAPI, así como una interfaz de usuario para poder probar. Gracias a ello, automáticamente se detectarán todos los controladores y sus acciones, y se añadirán de manera transparente el fichero json
y, por tanto, a la interfaz gráfica que lo utiliza.
Este es el comportamiento por defecto y la documentación básica. Entre nuestro deberes como developers, está el complementar esta documentación para que sea completa, añadiendo cosas como códigos HTTP esperados, autenticaciones si las hay, no mostrar todos los endpoints que no sean necesarios, etc..
Pese a todo, es una herramienta la mar de interesante y que personalmente pienso (y no solo yo por lo que se ve en el cambio de .Net 5) que debería formar parte, por defecto, de todas nuestras APIs REST. Va a permitir que terceros se integren con nosotros, pero incluso para nosotros mismos durante las pruebas nos va a simplificar mucho el trabajo al no tener que estar usando Postman, curl, o cualquier otra herramienta similar.
Por otro lado, y esto es también algo muy interesante, OpenAPI está soportado por Azure API Management (y seguro que por otros proveedores cloud), por lo que si nuestra API REST genera el fichero json
, podemos utilizarlo para etapas posteriores del ciclo de vida de nuestro servicio.