Middleware en ASP.NET 6: condiciones para agregar middleware a la canalización

Esta es la Parte 4 de una serie de cuatro partes. Es posible que desees leer la Parte 1, la Parte 2 y la Parte 3 primero.
¡Bienvenido de nuevo! Hasta ahora, en esta serie, cubrimos los conceptos básicos de Middleware en aplicaciones .NET 6, mostramos cómo crear clases de Middleware personalizadas y discutimos por qué el orden de las operaciones de la canalización de Middleware es tan importante.
En esta parte final de la serie, mostraremos dos formas de ejecutar condicionalmente el middleware en la canalización: usando la configuración en el archivo AppSettings.json para determinar si agregar o no el middleware a la canalización en primer lugar, o usando los datos de la solicitud entrante para ejecutar condicionalmente una pieza de middleware que ya está en proceso.
Middleware condicional basado en AppSettings
¿Recuerda la clase TimeLoggingMiddleware
de la Parte 3? Si no, aquí está de nuevo.
using MiddlewareNET6Demo.Logging;
using System.Diagnostics;
namespace MiddlewareNET6Demo.Middleware
{
public class TimeLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILoggingService _logger;
public TimeLoggingMiddleware(RequestDelegate next,
ILoggingService logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
Stopwatch watch = new Stopwatch();
watch.Start();
await _next(context);
watch.Stop();
_logger.Log(LogLevel.Information, "Time to execute: " + watch.ElapsedMilliseconds + " milliseconds.");
}
}
}
Vamos a decir, en esta publicación, que solo queremos agregar TimeLoggingMiddleware
a la canalización de la aplicación bajo ciertas condiciones. Esas condiciones pueden ser cuando estamos rastreando un error, o el cliente se ha quejado de la lentitud en la aplicación, o algo más.
Para agregar condicionalmente una pieza de middleware a la canalización, es conveniente configurar una sección en nuestro archivo AppSettings.json
que pueda ser leída por el archivo Program.cs
.
Agreguemos una sección llamada MiddlewareSettings
y una propiedad llamada UseTimeLoggingMiddleware
a nuestro archivo AppSettings.json
.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"MiddlewareSettings": {
"UseTimeLoggingMiddleware": "true",
}
}
También necesitamos una clase de C#
que pueda contener el valor de esta configuración. Por convención, el nombre de la clase debe coincidir con el nombre de la sección MiddlewareSettings
y el nombre de la propiedad debe coincidir con el nombre del elemento UseTimeLoggingMiddleware
.
namespace MiddlewareNET6Demo
{
public class MiddlewareSettings
{
public bool UseTimeLoggingMiddleware { get; set; }
}
}
Luego, en nuestro archivo Program.cs
, podemos leer esa sección de AppSettings.json
y asignarla a una instancia de la clase MiddlewareSettings C#
:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddTransient<ILoggingService, LoggingService>();
var app = builder.Build();
var middlewareSettings = builder.Configuration.GetSection("MiddlewareSettings").Get<MiddlewareSettings>();
//...Rest of Program.cs
Usando la nueva instancia de middlewareSettings
, podemos agregar TimeLoggingMiddleware
a la canalización solo si UseTimeLoggingMiddleware
es true
:
//...Rest of Program.cs
if(middlewareSettings.UseTimeLoggingMiddleware)
app.UseTimeLoggingMiddleware();
//...Rest of Program.cs
De esta forma, podemos controlar qué middleware está activo en la canalización en función de la configuración de toda la aplicación. Esto podría facilitar, por ejemplo, registrar los tiempos de ejecución cuando se intenta mejorar el rendimiento de la aplicación.
Middleware condicional basado en la URL de solicitud
Esta próxima forma de ejecutar middleware condicionalmente es un poco engañosa; el middleware aquí siempre se agregará a la canalización, pero no siempre hará otra cosa que pasar la ejecución a la siguiente pieza de middleware.
Digamos que tenemos una nueva clase de middleware llamada CultureMiddleware
:
using System.Globalization;
namespace MiddlewareNET6Demo.Middleware
{
public class CultureMiddleware
{
private readonly RequestDelegate _next;
public CultureMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var cultureQuery = context.Request.Query["culture"];
if (!string.IsNullOrWhiteSpace(cultureQuery))
{
var culture = new CultureInfo(cultureQuery);
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
}
await _next(context);
}
}
}
Ten en cuenta que este middleware solo realiza algún trabajo real si existe un parámetro de solicitud cultural en la solicitud entrante. Si ese parámetro existe, el middleware establece la referencia cultural actual de la aplicación al valor del parámetro entrante.
Por ejemplo, si enviamos la siguiente solicitud:
http://ourDemoSite.com/Users/174/Details?culture=fr-FR
CultureMiddleware establecería la cultura de la aplicación en fr-FR
, que es la cultura de Francia, y luego el procesamiento normal llevaría al usuario a cualquier página o ubicación representada por la parte Usuarios/174/Detalles de la URL.
Si la solicitud fuera
http://ourDemoSite.com/Invoices/Details/1235267376?culture=uk
Entonces el middleware establece la cultura en el Reino Unido y luego procesa normalmente.
Si, por el contrario, la solicitud entrante fuera
http://ourDemoSite.com/Subscriptions/v4nd37c/matthew-jones
Luego, el middleware, efectivamente, no hace nada y la cultura de la aplicación sigue siendo la cultura predeterminada.
Podemos usar la totalidad de la Solicitud entrante para este tipo de lógica. Por ejemplo, solo podríamos ejecutar el código para TimeLoggingMiddleware
si la solicitud entrante contenía ciertos valores. Si estuviéramos usando una aplicación MVC
, por ejemplo, tal vez solo queramos registrar los tiempos de ejecución para un solo controlador. En Razor Pages, tal vez solo una página determinada tenga problemas. Tal vez solo un endpoint en una API sea lento. En cualquiera de estos casos, podríamos usar esta metodología para orientar el TimeLoggingMiddleware exactamente a lo que queremos registrar.
En resumen, este método de ejecución condicional de middleware nos brinda un control más detallado sobre qué ejecutar, con el costo potencial de tener que insertar siempre el middleware en la canalización.
¡Gracias por leer! y ¡Feliz codificación!