Con la llegada de .NET 6 (preview 7 a día de redacción) y C# 10.0 podemos encontrar features muy interesantes, entre ellas la creación de minimal apis. No es ni más ni menos que una API con una cantidad de código muy reducida respecto a la versión habitual sacando los Controllers de la ecuación.
A diferencia de otros lenguajes, crear una API en C# tiene un “ritual” en el que en el Program.cs configuramos lo relativo al host ( si no lo tenemos en un proyecto aparte, que es lo recomendable ) y especificamos que vamos a usar nuestra clase Startup.
Dentro del Startup es donde configuramos los servicios, controladores, autenticación, etc. No es un proceso complejo cuando ya sabes como funciona, pero tampoco es simple.
En Python usando FastAPI creamos una API con unas pocas líneas de código como podemos ver en el ejemplo de Enrique Catalá Fastapi-Helloworld donde en poco más de 30 líneas de código levanta una api con tres métodos.
Con .NET 6 y C# 10.0 podemos hacer algo similar a como lo haríamos en Python, incluso con menos código.
Vamos a crear dos proyectos de API uno con .NET 5 y otro con .NET 6 para compararlos.
Estos son los ficheros que genera la plantilla para las diferentes versiones .NET6 y .NET 5.
Usando la plantilla webapi que trae dotnet vemos que la versión .NET 6 ya no trae el Startup.cs pero nos sigue generando controladores que podemos quitar para minimizar la API aún más.
En la versión de .NET 5 vemos el contenido al que ya estamos acostumbrados en el Startup.cs
Al igual que en el Program.cs donde esta la configuración básica del proceso de hosting:
Hasta aquí a lo que estamos acostumbrados, pero ¿Qué contiene la plantilla .NET 6 webapi?
Aquí ya estamos viendo varias features nuevas, la primera es la importación implícita de namespaces que nos ahorrará tener que incluir los usings más típicos.
Dependiendo del tipo de SDK que tenga nuestro proyecto se incluirán unos u otros.
SDK | Default namespaces |
Microsoft.NET.Sdk | System System.Collections.Generic System.IO System.Linq System.Net.Http System.Threading System.Threading.Tasks |
Microsoft.NET.Sdk.Web | System.Net.Http.Json Microsoft.AspNetCore.Builder Microsoft.AspNetCore.Hosting Microsoft.AspNetCore.Http Microsoft.AspNetCore.Routing Microsoft.Extensions.Configuration Microsoft.Extensions.DependencyInjection Microsoft.Extensions.Hosting Microsoft.Extensions.Logging |
Microsoft.NET.Sdk.Worker | Microsoft.Extensions.Configuration Microsoft.Extensions.DependencyInjection Microsoft.Extensions.Hosting Microsoft.Extensions.Logging |
La segunda feature “nueva” que podemos ver (la han mejorado en C# 10 pero viene de C# 9) es el top-level statements que nos permite eliminar la necesidad de generar una clase con un método static que reciba el string[] args y acerca más el desarrollo de aplicaciones a la estrategia de las Azure Functions y transforma nuestra API o aplicación en un “script” con muchas comillas.
Como nota adicional, solo podemos tener un fichero “top-level” no como en las Azure Functions.
Ahora bien, la plantilla por defecto sigue implementando un controlador y la idea de crear un minimal API es poder quitar los controladores de toda la ecuación. Para eso tenemos la nueva sobrecarga de la API Map.
El contenido del controlador WeatherForecastController es el siguiente:
Partiendo del Program.cs que nos proporciona la plantilla vamos a quitar las llamadas a builder.Services.AddController() y app.MapControllers() ya que no los vamos a utilizar y en su lugar llamaremos a builder.Services.AddEnpointsApiExplorer() para que Swagger siga funcionando.
De paso eliminamos la carpeta Controllers con su contenido, ¡hasta luego controladores!
Como en el ejemplo no vamos a usar autorización para el acceso a los métodos de la API quitamos también app.UseAuthorization().
Ahora añadimos el siguiente código a nuestro Program.cs:
Pero para que esto funcione, debemos añadir el using de nuestro namespace que es donde tenemos definida la clase WeatherForecast y se nos queda un Program.cs muy cuco.
34 líneas de código, nada mal.
En este momento ya hemos pasado el template de webapi de controladores a minimal API, pero ¿esto es aplicable a proyectos reales?
Hasta la llegada de C# 10 nos podía valer para hacer alguna prueba, etc. pero con la nueva feature de mejora de funciones lambda la cosa cambia. Esta feature nos permite añadir atributos a las funciones lambda y podemos usar por ejemplo el [Authorize] en la definición de la función o el [FromBody] a los parámetros de la función.
Además la versión preview de app.MapGet, app.MapPost,… inyectarán los parámetros como hacía en los controladores con lo que podemos extraer la lógica del método de la API a servicios e inyectarlos a nuestro método.
Vamos a probarlo creando un HelloWorldService:
Añadimos el servicio:
y añadimos el siguiente código
Con esto creamos un método post que recibe el nombre a través del cuerpo del mensaje y un método Get que simplemente hace una llamada al servicio que se le inyecta. Además, para ejemplo pasamos el ILoggerFactory para ver por consola el hit de la request.
Hacemos un dotnet run –build del proyecto y vamos a la UI del Swagger para probarlo:
Con estas herramientas podremos hacer nuestros proyectos de API de esta manera.
Pero, ¿Nos aporta algo más? Pues sí, rendimiento.
En el artículo publicado en el devblogs de Microsoft sobre la preview 4 de .NET 6 [ASP.NET Core updates in .NET 6 Preview 4 | ASP.NET Blog (microsoft.com)] donde comparan el rendimiento de las APIs con el nuevo routing vs APIs con MVC (Controladores) el nuevo routing es capaz de lograr 800k RPS (Request Per Second) mientras que los controladores 500k RPS usando el benchmark JSON de TechEmpower
Es una mejora bastante notable como para tenerlo en cuenta a la hora de elegir por donde afrontar un nuevo proyecto.
Tienes el código del ejemplo en nuestro GitHub
Conclusión
Por ir recapitulando, con la salida de .NET 6 en noviembre de 2021 (fecha prevista) recibiremos una mejora en rendimiento reduciendo la complejidad para crear APIs con una curva de aprendizaje menos agresiva.
Esto es lo que sabemos a día de hoy de lo que traerá .NET 6 y quedan un par de meses para la primera release, pero buena pinta tiene. ¡Habrá que estar atentos a ver qué novedades trae!
Guillermo Pérez
Data & AI Technical Lead en Verne Technology Group