Menú de navegaciónMenú
Categorías

La mejor forma de Aprender Programación online y en español www.campusmvp.es

?id=5ac65f15-3177-491a-a176-b2eade2f360d

Novedades de ASP.NET Core 3.0

Logotipo no oficial de ASP.NET Core

Microsoft acaba de presentar, en el marco del evento .NET Conf 2019, la nueva versión de .NET Core y el conjunto de tecnologías y frameworks relacionados.

Si bien para ASP.NET Core no se trata de una versión especialmente revolucionaria, sí que viene acompañada de algunos breaking changes y novedades que vale la pena comentar.

Lo que vamos a ver no es una lista exhaustiva de los cambios introducidos con esta versión (¡para eso está la documentación oficial!), pero sí aquellos aspectos que más me han llamado la atención:

  • Simplificación del archivo de proyecto .csproj
  • Uso del host genérico
  • Endpoint routing
  • Cambios en el registro de servicios de MVC
  • Bye bye, JSON.NET!
  • Bye bye, target .NET Framework!
  • Limpieza de Microsoft.AspNetCore.App
  • Compilación de vistas
  • Soporte gRPC
  • Blazor Server-Side
  • Otras mejoras

Simplificación del archivo de proyecto .csproj

El SDK utilizado en los archivos de proyecto, Microsoft.NET.Sdk.Web, incluye ahora características por defecto que hacen innecesaria la especificación a nivel del .csproj de aspectos que anteriormente eran necesarios, como la referencia a Microsoft.AspNetCore.App y otros paquetes necesarios, o la habilitación del hosting in-process de IIS.

De esta forma, los archivos de proyecto se han simplificado aún más. Como muestra, este era el contenido de una aplicación ASP.NET Core MVC típica en ASP.NET Core 2.2:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
  </ItemGroup>

</Project>

Y así es como queda en ASP.NET Core 3.0, básicamente reducido a la indicación del Target Framework Moniker (TFM):

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.0</TargetFramework>
  </PropertyGroup>

</Project>

Host genérico

En versiones anteriores de ASP.NET Core, el arranque de la aplicación web estaba ligado irremediablemente a un WebHost. Eso hacía que muchas de las funcionalidades proporcionadas por el marco de trabajo fueran difícilmente utilizables fuera de ASP.NET Core.

Esto se ponía de manifiesto muy claramente si echábamos un vistazo al archivo Program.cs, donde directamente estábamos usando un IWebHostBuilder para crear el WebHost que lanzaba la aplicación:

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

En ASP.NET Core 3.0, esto ha mejorado bastante gracias a la puesta en valor del host genérico, un componente que ya se utilizaba para entornos no web en versiones anteriores del framework, y que ahora pasa a formar parte también de las aplicaciones ASP.NET Core, unificando así determinados aspectos que antes quedaban algo dispersos.

Este host actúa como un entorno de ejecución para aplicaciones de (virtualmente) cualquier tipo, proporcionándoles servicios básicos como el sistema de configuración, logging, lifetime management, o los servicios de inyección de dependencias. La idea es que estos servicios sean los mismos y se configuren de la misma forma, estemos programando una aplicación ASP.NET Core o un servicio para Windows.

Observad ahora, en ASP.NET Core 3.0, cómo cambia la cosa. En el Program.cs simplemente construimos y configuramos un host genérico, que "aderezamos" con particularidades de la web a través del extensor ConfigureWebHostDefaults():

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

Endpoint routing

Este es uno de los cambios de mayor calado en esta nueva versión del framework. El sistema de routing de ASP.NET Core ha sido reestructurado para convertirlo en una solución mucho más eficiente, potente y flexible, y desligado de componentes o tecnologías con un nivel de abstracción superior como MVC o SignalR.

En ASP.NET Core 3.0, se considera que un endpoint es cualquier cosa capaz de procesar peticiones. Puede ser un handler implementado en una lambda, una acción MVC, un hub SignalR, una página Razor o un servicio gRPC; desde el punto de vista del routing da lo mismo: es sólo un procesador de peticiones al que se puede llegar mediante una ruta.

Estos endpoints son registrados de forma global durante la inicialización de la aplicación utilizando métodos de configuración que facilitan bastante la tarea. Al finalizar, el sistema tendrá una visión clara de qué patrones de rutas soporta la aplicación y cómo y dónde pueden ser procesadas.

Por ejemplo, en el siguiente código, perteneciente al método Configure() de la clase Startup, vemos cómo registrar un handler inline en una lambda, endpoints para todas las acciones de una aplicación MVC, de las páginas Razor que tengamos en la carpeta /Pages, y de un hub SignalR:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
  ...
  app.UseRouting();
  ...
  app.UseEndpoints(endpoints =>
  {
      endpoints.MapGet("/hello", async context =>
      {
          await context.Response.WriteAsync("Hello World!");
      });
      endpoints.MapControllerRoute(
          name: "default",
          pattern: "{controller=Home}/{action=Index}/{id?}");
      endpoints.MapRazorPages();
      endpoints.MapHub<ChatHub>("/myhub");

  });
}  

Por tanto, ya no podremos utilizar el conocido UseMvc() para añadir el framework MVC al pipeline. Ahora, al pipeline añadimos el middleware encargado de ejecutar los endpoints, y en la configuración de éste es donde mapeamos las acciones.

Otros aspecto interesante, que ya he dejado entrever en el bloque de código anterior, es que, la tarea de decidir qué endpoint será el encargado de procesar cada petición entrante está separada de la tarea de ejecutarlo. Existe un middleware específico para cada una de estas tareas:

public void Configure(IApplicationBuilder app)
{
  ...

  // En este punto se decide qué endpoint procesará la petición:
  app.UseRouting();
  
  ... // Aquí irían otros middlewares

  // En este punto se ejecuta realmente el endpoint seleccionado:
  app.UseEndpoints(endpoints =>
  {
      endpoints.MapControllerRoute(
          name: "default",
          pattern: "{controller=Home}/{action=Index}/{id?}");
  });
}  

Esta separación de responsabilidades proporciona unas posibilidades que hasta ahora no teníamos: una vez se ha seleccionado el endpoint que procesará una petición, pueden existir middlewares intermedios que examinen al candidato y tomen decisiones antes de ser ejecutado. Por ejemplo, el nuevo sistema de autorización funciona de esta forma: analiza si el endpoint seleccionado puede ser ejecutado por el usuario actual, y, en caso contrario, fuerza una redirección hacia la URL que le hayamos indicado.

Fijaos que esto hace posible que la gestión de algunos aspectos transversales, como la autorización, CORS y otros, sea llevada a cabo por completo desde la infraestructura de ASP.NET Core y no desde implementaciones específicas existentes en el interior de frameworks como MVC.

Cambios en el registro de servicios de MVC

Profundizando la línea de modularidad que definió desde sus principios todo lo relacionado con ASP.NET Core, en la versión 3.0 encontramos nuevos extensores para registrar los servicios de MVC en el sistema de inyección de dependencias, de forma más granular.

Hasta ahora, usábamos algo como lo siguiente:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
}

Con esa simple llamada, registrábamos todo lo necesario para que funcionara el framework MVC (controladores, vistas) y Razor Pages. Sin embargo, esto era un exceso para escenarios más específicos, como aplicaciones que actuaban exclusivamente como APIs, o sistemas que utilizaban MVC y no Razor Pages, o viceversa.

Para ello, se han introducido los siguientes extensores de IServiceCollection:

  • AddControllers(), para registrar los componentes necesarios para ejecutar exclusivamente acciones, por ejemplo en una aplicación API donde no vamos a necesitar vistas MVC ni Razor Pages.
  • AddControllersWithViews(), registrará los componentes necesarios para ejecutar acciones, pero también para poder procesar vistas Razor.
  • AddRazorPages(), por último, si queremos registrar los servicios requeridos para ejecutar Razor Pages.

Por tanto, el método ConfigureServices() de una aplicación típica MVC podría quedar como sigue:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersAndViews();
}

Nota: AddMvc() sigue existiendo, y equivale a llamar de forma consecutiva a AddControllersWithViews() y AddRazorPages(), siendo así compatible con versiones anteriores de ASP.NET Core.

¡Bye bye, JSON.NET!

Pues sí, la biblioteca de serialización/deserialización de JSON más popular para aplicaciones .NET pasa a ser un componente opcional, sólo requerido por razones de retrocompatibilidad o cumplimiento de dependencias de otros componentes.

Pero no nos quedamos huérfanos 😉 A partir de ahora, por defecto ASP.NET Core utilizará internamente el nuevo paquete NuGet System.Text.Json, que de serie formará parte de Microsoft.AspNetCore.App.

El uso de este nuevo componente aporta grandes ventajas:

  • Dado que ahora las herramientas de serialización y deserialización JSON serán proporcionadas por la plataforma, se ha podido eliminar la dependencia de JSON.NET en muchos componentes del framework y de terceras partes. Esto ha sido, y aún es, fuente de numerosos problemas e incompatibilidades entre versiones (si lo has sufrido, sabrás de lo que hablo).
  • Aumento del rendimiento, entre 1.5 y 5 veces superior a JSON.NET, entre otras cosas gracias al uso intensivo de recientes incorporaciones al framework, como los tipos Span<T> y relacionados.

Y en cuanto al uso, tampoco es que cambie demasiado. Así es como serializaríamos un objeto con la nueva versión:

string serializedInvoice = JsonSerializer.Serialize(invoice);

¡Bye bye, .NET Framework!

Las primeras versiones de ASP.NET Core podían correr tanto sobre .NET Core como .NET Framework. Estratégicamente era bastante apropiado, pues el ecosistema de .NET Core (bibliotecas, paquetes NuGet...) estaba bastante verde, y la única forma de conseguir que los desarrolladores fueran introduciendo el nuevo framework era proporcionarles esta posibilidad.

Al mismo tiempo, .NET Standard fue estableciendo las directrices con las que debían alinearse tanto los componentes de terceros como los propios marcos de trabajo de Microsoft para ser interoperables, llevándonos al momento actual. Hoy, la mayoría de bibliotecas, o al menos las más populares, implementan .NET Standard y son utilizables directamente desde .NET Core, restando sentido a la posibilidad de ejecutar ASP.NET Core sobre .NET Framework.

Otro motivo de la retirada del soporte para el viejo marco de trabajo es que éste no va a evolucionar más. De hecho, .NET Framework 4.8, que es la última versión que aparecerá, ya no es compatible con la última versión de .NET Standard 2.1, por lo que podríamos considerar que, oficialmente, se ha quedado por detrás.

Limpieza de Microsoft.AspNetCore.App

En esta nueva versión han aprovechado para limpiar un poco Microsoft.AspNetCore.App, que incluía ensamblados que nada tenían que ver con ASP.NET Core.

Este cambio no supone pérdida de funcionalidad alguna, pues los ensamblados retirados de este shared framework aún siguen estando disponibles como paquetes NuGet independientes. Por si te interesa verlo con más detalle, la lista completa de ensamblados eliminados de Microsoft.AspNetCore.App está en este issue de GitHub.

Compilación de vistas

En esta versión de ASP.NET Core, las vistas son compiladas cuando debe ser: en tiempo de compilación. Los paquetes de publicación incluyen exclusivamente los ensamblados resultado de dicho proceso, y no las vistas o páginas Razor (archivos .cshtml). Hasta aquí es igual que en ASP.NET Core 2.2.

Lo que cambia es que la posibilidad de editar y compilar las vistas Razor en tiempo de ejecución es un opt-in que debemos activar expresamente durante el registro de servicios:

services
    .AddControllersWithViews()
    .AddRazorRuntimeCompilation();

Obviamente, aparte es necesario hacer que los archivos de vistas o páginas se incluyan al publicar, para lo cual será necesario introducir algunos cambios en el archivo de proyecto .csproj.

Soporte gRPC

ASP.NET Core incorpora por primera vez soporte para el estándar gRPC, tanto a nivel de framework como de tooling. Tendremos soporte para la edición de archivos .proto, generación de clientes, realización de mapeos de endpoints a handlers gRPC, y todo lo que necesitamos para crear clientes o servidores de este tipo de servicios.

gRPC, como sabréis, se trata de un marco de trabajo para la implementación, tanto en cliente como en servidor, de llamadas entre sistemas remotos (RPC) basado en tecnologías recientes como el transporte HTTP/2 y la serialización Protobuf.

¿Y qué ventajas tienen los servicios gRPC frente a los tradicionales APIs HTTP? Pues la primera, el rendimiento: al utilizar serialización binaria y HTTP/2, los paquetes de datos transmitidos pueden ser mucho menores. También, dado que los servicios de definen en archivos .proto, existe un contrato explícito que los clientes pueden utilizar para generar código de acceso.

Como punto negativo, lo que perdemos es la posibilidad de leer los mensajes directamente, como hacíamos con JSON, y su uso en algunos escenarios (por ejemplo, los browsers no soportan gRPC de forma nativa).

Puedes ver otras ventajas y desventajas en esta página la documentación oficial.

Como curiosidad, esta es la pinta que tiene el código cliente de un servicio gRPC:

static async Task Main(string[] args)
{
    var channel = GrpcChannel.ForAddress("https://localhost:5001");
    var client =  new Invoice.GreeterClient(channel);
    var reply = await client.SayHelloAsync(
        new HelloRequest { Name = "GreeterClient" }
    );
    Console.WriteLine("Greeting: " + reply.Message);
}

Blazor server-side

Para los que no estéis al tanto, Blazor es un framework de desarrollo frontend, con un enfoque bastante novedoso y sorprendente. Comenzó como un proyecto experimental, una serie de prototipos que fueron evolucionando hasta pasar a ser un proyecto oficial hace algunos meses.

El objetivo de Blazor es conseguir que los desarrolladores puedan crear aplicaciones SPA completas utilizando exclusivamente C#.

Inicialmente, se planteó llevando al browser un runtime de .NET basado en WebAssembly, que ejecutaría el ensamblado resultante de compilar el lado cliente del proyecto. Conforme el proyecto evolucionaba, se añadió también la posibilidad de ejecutar esta lógica en el servidor (Blazor Server), manteniendo una conexión SignalR para el intercambio de eventos, cambios en el UI o llamadas realizadas desde script.

Pues bien, con la nueva oleada de tecnologías Core 3.0 se presenta la primera versión oficial de Blazor, aunque, de momento sólo en uno de sus sabores: Blazor Server-side.

Otras mejoras

Aparte de las vistas anteriormente, citamos otras mejoras en el framework, algunas de ellas derivadas directamente de las novedades introducidas en .NET Core 3:

Por supuesto, en campusMVP llevamos varios meses trabajando en la actualización de nuestro mítico curso de ASP.NET Core por José María Aguilar y coincidiendo con el lanzamiento oficial de .NET Core 3, hemos lanzado la nueva versión del curso. ¡No te lo pierdas para estar a la última!

Y para más información, aquí os dejo algunos enlaces:

¡Que lo disfrutéis!

José María Aguilar José María atesora una amplísima experiencia trabajando en el mundo del desarrollo de software (programador, analista, responsable de informática, consultor, director técnico), principalmente con tecnologías Microsoft. Actualmente trabaja como consultor y desarrollador independiente, ofreciendo servicios tecnológicos a empresas e instituciones.
Es un reconocido experto en desarrollo web en todo el mundo, y es autor del libro de Microsoft Press "SignalR Programming in Microsoft ASP.NET".
Escribe regularmente artículos sobre ASP.NET MVC y otros temas relacionados con el desarrollo de software en su blog.
Puedes seguirlo en Twitter en @jmaguilar. Ver todos los posts de José María Aguilar
Archivado en: Lenguajes y plataformas

¿Te ha gustado este post?
Pues espera a ver nuestro boletín mensual...

Suscríbete a la newsletter

La mejor formación online para desarrolladores como tú

Comentarios (2) -

David López
David López

¿Y Visual Basic? ¿Sigue siendo todavía el patito feo?

Responder

Hola David:

En teoría esta versión de .NET Core iba a traer un buen soporte de VB.NET (devblogs.microsoft.com/.../), sin embargo la cosa se ha quedado muy descafeinada (devblogs.microsoft.com/.../#comment-3220) y hay alguna gente enfada con esto (devblogs.microsoft.com/.../#comment-3286).

Sinceramente la apuesta de futuro en lenguajes .NET debería ser C# y si acaso para ciertas aplicaciones F#, pero VB, como bien dice,s parece el patito feo.

Si quieres puedes "votar" para que se añada soporte de VB.NET a WPF y Windows Forms en .NET Core 3.0 aquí: developercommunity.visualstudio.com/.../...ws.html Pero buena suerte con ello...

Saludos!

Responder

Agregar comentario

Los datos anteriores se utilizarán exclusivamente para permitirte hacer el comentario y, si lo seleccionas, notificarte de nuevos comentarios en este artículo, pero no se procesarán ni se utilizarán para ningún otro propósito. Lee nuestra política de privacidad.