Como siempre, de la mano de las novedades de .Net, llegan las novedades de Asp.Net. En este caso la versión Preview 3. Veamos las novedades más importantes de estas versión:
- Compatibilidad con filtros de controlador de rutas en Minimal API.
- Capacidad de prueba unitaria mejorada para manejadores de Minimal API.
- Enlazar mediante TryParse para controladores MVC y API.
- Nuevas sobrecargas: Results.Stream().
- Rendimiento HTTP/2 mejorado cuando se utilizan muchas secuencias en una conexión.
- Nuevo evento ServerReady para medir el tiempo de inicio
- Modo oscuro de la página de excepción del desarrollador
Puedes comenzar a utilizar esta preview instalando el SDK desde este link. Si estás utilizando el sistema operativo Windows se recomienda utilizar la versión de Visual Studio 2022 preview. En otros sistemas operativos no se encuentra disponible por el momento.
Compatibilidad con filtros de controlador de rutas en Minimal API
Se agregó el soporte para filtros en controladores en Minimal API. Los filtros se ejecutan antes de la lógica del controlador de ruta principal y se pueden usar para inspeccionar y modificar los parámetros del controlador o interceptar la ejecución del controlador.
Hay varias formas de registrar los filtros, por ejemplo, registrar un filtro mediante un RouteHandlerFilterDelegate y AddFilter método de extensión de la siguiente manera:
string HelloName(string name) => $"Hello, {name}!";
app.MapGet("/hello/{name}", HelloName)
.AddFilter(async (routeHandlerInvocationContext, next) =>
{
var name = (string) routeHandlerInvocationContext.Parameters[0];
if (name == "Bob")
{
return Results.Problem("No Bob's allowed");
}
return await next(routeHandlerInvocationContext);
});
También es posible hacerlo por medio de una estrategia factory por medio de RouteHandlerContext y MethodInfo asociado con el controlador y los metadatos registrados en el extremo.
app.MapGet("/hello/{name}", HelloName)
.AddFilter((routeHandlerContext, next) =>
{
var parameters = routeHandlerContext.MethodInfo.GetParameters();
var hasCorrectSignature = parameters.Length == 1 && parameters[0].ParameterType == typeof(string);
return async (routeHandlerInvocationContext) =>
{
if (hasCorrectSignature)
{
var name = (string) routeHandlerInvocationContext.Parameters[0];
if (name == "Bob")
{
return Results.Problem("No Bob's allowed");
}
}
return await next(routeHandlerInvocationContext);
};
})
Por último, los filtros se pueden implementar la interfaz IRouteHandlerFilter y resolverlo por DI o pasado como una instancia.
app.MapGet("/hello/{name}", HelloName)
.AddFilter<MyFilter>();
Pruebas unitarias mejoradas para handlers
IResult los tipos de implementación ahora están disponibles públicamente en el espacio de nombres Microsoft.AspNetCore.Http con el sufijo HttpResult (OkObjectHttpResult, ProblemHttpResult, etc.). Con estos, ahora se pueden realizar pruebas unitarias más fácilmente en los controladores de ruta mínimos cuando usamos métodos con nombre en lugar de lambdas.
[Fact]
public async Task GetTodoReturnsTodoFromDatabase()
{
var todo = new Todo { Id = 42, Name = "Improve Results testability!" };
var mockDb = new MockTodoDb(new[] { todo });
var result = (OkObjectHttpResult)await TodoEndpoints.GetTodo(mockDb, todo.Id);
//Assert
Assert.Equal(200, result.StatusCode);
var foundTodo = Assert.IsAssignableFrom<Models.Todo>(result.Value);
Assert.Equal(id, foundTodo.Id);
}
[Fact]
public void CreateTodoWithValidationProblems()
{
//Arrange
var newTodo = default(Todo);
var mockDb = new MockTodoDb();
//Act
var result = TodoEndpoints.CreateTodo(mockDb, newTodo);
//Assert
var problemResult = Assert.IsAssignableFrom<ProblemHttpResult>(result);
Assert.NotNull(problemResult.ProblemDetails);
Assert.Equal(400, problemResult.StatusCode);
}
Enlazar usando TryParse en MVC y API Controllers
ahora se puede enlazar los valores de los parámetros de acción del controlador mediante un TryParse:
public static bool TryParse(string value, T out result);
public static bool TryParse(string value, IFormatProvider provider, T out result);
En el siguiente controlador, en el GETm se enlazan los datos de la cadena de consulta mediante un TryParse método en el tipo de parámetro:
public class TryParseController : ControllerBase
{
// GET /tryparse?data=MyName
[HttpGet]
public ActionResult Get([FromQuery]CustomTryParseObject data) => Ok();
public class CustomTryParseObject
{
public string? Name { get; set; }
public static bool TryParse(string s, out CustomTryParseObject result)
{
if (s is null)
{
result = default;
return false;
}
result = new CustomTryParseObject { Name = s };
return true;
}
}
}
Nuevas sobrecargas en Results.Stream()
Nuevas sobrecargas en Results.Stream(…) para adaptarse a escenarios en los que necesita acceso a la secuencia de respuesta HTTP subyacente sin almacenamiento en búfer. Estas sobrecargas también mejoran los casos en los que la API desea transmitir datos a la secuencia de respuesta HTTP, como desde Azure Blob Storage.
app.MapGet("/process-image", async (HttpContext http) =>
{
using var image = await Image.LoadAsync("puppy.jpeg");
int width = image.Width / 2;
int height = image.Height / 2;
image.Mutate(x => x.Resize(width, height));
http.Response.Headers.CacheControl = $"public,max-age={FromHours(24).TotalSeconds}";
return Results.Stream(stream => image.SaveAsync(stream, PngFormat.Instance), "image/png");
});
Rendimiento HTTP/2 mejorado cuando se utilizan muchas secuencias en una conexión
Se realizaron cambios en el código de escritura de tramas HTTP/2 mejorando el rendimiento cuando hay varias secuencias que intentan escribir datos en una sola conexión HTTP/2. Ahora se envía el trabajo TLS al grupo de subprocesos y se libera más rápidamente un bloqueo de escritura que otras secuencias pueden adquirir para escribir sus datos. La reducción en los tiempos de espera, en algunos casos, mejora en el rendimiento donde hay contención para este bloqueo de escritura. Un punto de referencia gRPC con 70 flujos en una sola conexión (con TLS) mostró una mejora de ~ 15% en las solicitudes por segundo (RPS) con este cambio.
Nuevo evento para medir el tiempo de inicio: ServerReady
Si utilizamos EventSource para métricas/diagnósticos y deseamos medir el tiempo de inicio de su aplicación ASP.NET Core, ahora podemos utilizar el nuevo evento ServerReady que es parte de Microsoft.AspNetCore.Hosting que representa el punto en el que el servidor está en funcionamiento
Modo oscuro de la página de excepción del desarrollador
La página de excepción para desarrolladores de ASP.NET Core ahora admite el modo oscuro:
Conclusiones
Tenemos cosas muy interesantes en esta preview para Asp.Net, próximamente tendremos muchas novedades de la mano de Blazor. No te pierdas los siguientes posts, veremos todo lo que viene en la próxima versión de .Net.