Este pasado mes de noviembre salió la nueva version de netCore, .NET 6. Vamos a explotar una de sus nuevas funcionalidades. la cual nos permite crear un API minimalista, con muy pocas líneas de código y un mejor rendimiento.
Haremos una pequeña comparación entre net 5 y net 6, explicaremos los puntos más importantes de esta nueva funcionalidad, y veremos como podemos añadir distintas funcionalidades, para intentar explotar al máximo esta funcionalidad.
¿Qué son las minimal API’S?
Como se intuye por su nombre, no es ni mas ni menos que un API que podemos construir con muy pocas líneas de código y en un único fichero.
Para los conocedores de Python, esto nos recuerda a FastAPI donde con apenas 30 líneas de código tendriamos un API funcional. Sin embargo como veremos más adelante con net 6 podemos tenerlo de una forma mucho mas compacta y seremos capaces de construir un api funcional con menos de 10 líneas de código. Lo que nos va a proporcionar un medio para construir api’s de forma rápida, con un buen rendimiento en un único archivo de código. Esta nueva funcionalidad es idonea para microservicios con unos pocos endpoints en los que se tenga una funcionalidad muy reducida.
Comparación estructuras
Para un proyecto tipico de WebApi en .NET 5, tenemos una estructura de proyecto similar a la siguiente:
[scss]WebApi.Net5
| appsettings.json
| Program.cs
| Startup.cs
| WebApi.Net5.csproj
|
+—Controllers
| ParticlesController.cs
|
+—Properties
launchSettings.json
[/scss]
Donde tenemos el Program.cs donde configuraremos el host y el Startup.cs donde se configura la propia aplicación, cabe destacar que la buena práctica en una webApi clásica, lo recomendable es tener la configuración relativa al host en un proyecto distinto al proyecto del API, ya que nos facilitaría hospedar la misma de diferentes formas solo cambiando el punto de entrada. En .NET 6 cambia un poco, y es aqui donde toma importancia las minimal API’S, ya que podemos construir nuestra aplicación directamente en la clase Program.cs. Tendriamos una estructura similar a esta:
[scss]WebApi.Net6
| appsettings.json
| Program.cs
| WebApi.Net6.csproj
| +—Properties
launchSettings.json
[/scss]
Vemos que con net 6, quitando los archivos de configuración y el de proyecto, tenemos exclusivamente el Program.cs, que es el archivo donde tendremos todo el código de la aplicación.
Generación proyecto
Este proyecto lo podemos generar con las nuevas plantillas de net 6. Vamos a la la ruta donde queremos generar nuestro proyecto, y ejecutamos:
dotnet new web
Ya tendríamos nuestro proyecto listo para funcionar. Vamos a ver mas en detalle el contenido de este Program y lo mínimo e imprescindible para construir un API.
[scss]var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet(«/», () => «Hello World!»);
app.Run();
[/scss]
Se aprecian varias de las mejoras de net6, vemos que se han eliminado los típicos using y se aprecia la sintaxis de top-level statments, que nos permite eliminar la generación de na clase un método main para el inicio de nuestra aplicación.
Tenemos el nuevo WebApplication de Microsoft.AspNetCore.Builder, que nos provee de lo necesario para crear el Host. Y los métodos Map, que nos sirven para enrutar nuestros endpoints, eliminando la necesidad de tener controladores para ello. Los principales métodos de EndpointRouteBuilderExtensions, son los siguientes:
- MapGet() -> para métodos GET
- MapPost() -> para métodos POST
- MapPut() -> para métodos PUT
- MapDelete() -> para métodos DELETE
- MapMethods() -> para varios métodos a la vez
Todos ellos admiten como parametros string con la ruta del endpoint, y Delegados para indicar que función va a realizar el método. El ejemplo mas simple sería el que hemos mostrado para el GET, un poco mas arriba.
Construyendo rutas
Vamos a ver como podemos pasar parametros a estos métodos, ya que realmente nos sería de poca utilidad no poder pasarlos. Construyamos un GET, donde le vamos a pasar por ruta un nombre y nos devolverá un saludo a ese nombre.
[scss]app.MapGet(«/hello/{name}», () => $»Hello {name}»);[/scss]
Si invocamos la ruta https://localhost/hello/Aitor, nos devolvera un Hello Aitor.
También podemos pasar un objeto a un metodo POST, para que fuese guardado en base de datos por ejemplo.
[scss]
app.MapPost(«/article», (Article article) =>
{
SaveArticle(article);
return Results.Ok(article);
});
[/scss]
Aquí ya vemos que estamos devolviendo una respuesta Http.Results, ya que dentro de la lógica podemos devolver el resultado correcto en función del negocio desencadenado. Más adelante veremos algún método más completo, con mayor lógica.
Podríamos tener métodos asíncronos, es mas, suele ser lo habitual. Para ellos solo bastaría con introducir un async antes de nuestra función landa, y dentro de ella, tener lógica asíncrona. Por ejemplo, vamos a usar el servicio de fake API {JSON} Placeholder para que el GET nos devuelva un objeto asíncronamente. Sería tan sencillo como esto:
[scss]app.MapPost(«/getasync», async () =>
{
HttpClient client = new HttpClient();
var response = await
client.GetAsync(«https://jsonplaceholder.typicode.com/todos/1»);
response.EnsureSuccessStatusCode();
return response.Content.ReadAsStringAsync();
});
[/scss]
De esta manera ya tendríamos un Get asíncrono, y de igual manera lo podríamos construir para el resto de los métodos (get, post, put,…)
OpenApi
Aunque las cosas han cambiado con net6, no perdemos ciertas funcionalidades que son muy interesantes. Podemos hacer uso de swagger para una interfaz legible de nuestro API y ayuda a la documentación de la misma.
En el momento de escribir este artículo, se encuentra disponible la version 6.2.3 de Swashbuckle.AspNetCore.
Podemos instalar el nuget como mas nos guste, típicamente cualquiera de las siguientes formas, valdría:
Install-Package Swashbuckle.AspNetCore -Version 6.2.3
Y dentro de nuestro proyecto solo nos quedaría añadir el servicio de swagger al serviceColletion y el middleware para mostrar el UI de swagger.
Cabe destacar que antes de añadir builder.Services.AddSwaggerGen(); es importante, añadir el descubrimiento de los metadatos de nuestros endpoints. Esto lo podemos hacer de la siguiente manera: builder.Services.AddEndpointsApiExplorer();
De modo que nuestro api, quedaría de la siguiente manera, una vez añadido Swagger:
[scss]var builder = WebApplication.CreateBuilder(args);
//Services
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
//Routes
app.MapGet(«/minimal», () => «Get method»);
app.MapPost(«/minimal», () => «Post method»);
app.MapPut(«/minimal», () => «Put method»);
app.MapDelete(«/minimal», () => «Delete method»);
//Middleware
app.UseSwagger();
app.UseSwaggerUI();
app.Run();[/scss]
Si arrancamos la aplicación, tendremos lo siguiente:
Las minimal apis, no nos impiden usar el reto de frameworks conocidos, ni mucho menos. En posteriores artículos, veremos mas en detalle como podríamos añadir a un pequeño proyecto EntityFrameworkCore, añadir inyección de dependencias, explotar al máximo la funcionalidad de Swagger o como añadir autorización.
Conclusiones
Cabe destacar que estas apis minimalistas están orientadas para proyectos pequeños, donde por ejemplo tengamos un solo controlador con unos cuantos endpoints. Pero a todos nos ha pasado que hemos hecho un proyecto con una infraestructura enorme para disponer de 2 o 3 endpoints para una funcionalidad muy especifica. Gracias a la llegada de .NET 6, tenemos una solución simplista.
También cabe destacar que el rendimiento tiene una mejoría notable. Según el Benchmarck de TechEmpower el nuevo enrutamiento es capaz de conseguir 800k RPS, frente a los 500k RPS del uso tradicional de controladores en MVC.
Aunque con esta nueva funcionalidad, todo parecen ventajas. En mi opinión existen también varias desventajas, no todo va a ser perfecto. Al tener todo en un único fichero, debemos de tener cuidado, ya que fácilmente se nos puede descontrolar el tamaño del mismo. Por tanto en mi opinión esta es una solución válida únicamente para un proyecto muy acotado, con pequeñas funcionalidades y que no necesiten de una gran infraestructura. En caso contrario sería recomendable desarrollar un API mvc clásica.
Además hay que tener en cuenta que con los lenguajes top-level rompemos en gran medida el Principio de Responsabilidad Única de SOLID, ya que en el mismo fichero estamos incluyendo todas las responsabilidades del proyecto.
Si quieres ver o descargar el código correspondiente a este artículo, está disponible en el siguiente enlace minimal-apis-sample.
Siente libre de hacer cualquier aportación al mismo.