A veces, sobre todo en aplicaciones muy grandes, con las definiciones de rutas muy complejas o cuando nos toca analizar aplicaciones ajenas, puede ser interesante saber qué punto del código está procesando una petición determinada, ya sea un endpoint definido usando Endpoint Routing o Minimal APIs o bien sea una acción de un controlador MVC.
En este post vamos a ver cómo conseguirlo de forma muy sencilla, mediante la implementación un pequeño middleware que, insertado en el pipeline, añadirá en el encabezado información sobre el handler que generó la respuesta de la petición actual.
Un pequeño middleware que "espía" el sistema de routing
Como probablemente sabréis, el sistema de routing de ASP.NET Core está implementado en dos middlewares:
EndpointRoutingMiddleware
, que, analiza el path de las peticiones entrantes y, en función de las rutas registradas en la aplicación, establece el endpoint al que se delegará su proceso.EndpointMiddleware
, que obtiene el endpoint establecido por el middleware anterior y se encarga de ejecutarlo.
En ASP.NET Core 5 y anteriores estos middlewares debíamos introducirlos manualmente en el pipeline, pero gracias a las nuevas abstracciones y convenciones introducidas por minimal hosting en ASP.NET Core 6, esto se realiza de forma automática.
Por tanto, si introducimos en el pipeline un middleware personalizado que tome el control justo después de que EndpointRoutingMiddleware
haya realizado su trabajo, podremos conocer qué parte del código va a procesar cada una de las peticiones entrantes.
En el siguiente código vemos cómo conseguirlo; podéis observar que usamos el extensor GetEndpoint()
del contexto HTTP para conocer el endpoint seleccionado por EndpointRoutingMiddleware
, e incluimos su nombre descriptivo en un encabezado personalizado de la respuesta:
app.Use(async (ctx, next) =>
{
var endpoint = ctx.GetEndpoint();
if (endpoint != null && !ctx.Response.HasStarted)
{
ctx.Response.Headers.Add("X-Handled-By", endpoint.DisplayName);
}
await next(ctx);
});
Fijaos que usamos
Response.HasStarted
para determinar si la respuesta ya ha comenzado. En caso contrario, no sería posible añadir encabezados porque quizás estos ya fueron enviados al cliente.
Vamos a ver qué nos encontraremos en los encabezados en varios escenarios. Para ello, creamos un proyecto ASP.NET Core MVC y sustituimos su Program.cs
por el siguiente código, donde implementamos mapeos de ruta con distintos tipos de handlers:
Verbo | Ruta | Tipo de Manejador | Manejador |
---|---|---|---|
GET | /inline | Delegado en línea | Delegado definido en app.MapGet() |
GET | /local | Función local | LocalFunctionHandler() |
GET | /static | Método estático | Handlers.StaticMethodHandler() |
GET | /instance | Método de instancia | Handlers.StaticMethodHandler() |
GET | /home/index | Acción MVC | HomeController.Index() |
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var app = builder.Build();
// Definición de endpoints
app.MapGet("/inline", () => "Hello world!");
app.MapGet("/local", LocalFunctionHandler);
app.MapGet("/static", Handlers.StaticMethodHandler);
app.MapGet("/instance", new Handlers().InstanceMethodHandler);
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
// Middleware "espía"
app.Use(async (ctx, next) =>
{
var endpoint = ctx.GetEndpoint();
if (endpoint != null && !ctx.Response.HasStarted)
{
ctx.Response.Headers.Add("X-Handled-By", endpoint.DisplayName);
}
await next(ctx);
});
app.Run();
// Handlers
string LocalFunctionHandler() => "Hello from a function!";
class Handlers
{
public static string StaticMethodHandler() => "Hello from a function!";
public string InstanceMethodHandler() => "Hello from a function!";
}
Si enviamos peticiones hacia los endpoints definidos, podremos observar los siguientes valores en el encabezado X-Handled-By
:
Petición | Valor de encabezado |
---|---|
GET /inline | X-Handled-By: HTTP: GET /inline |
GET /local | X-Handled-By: HTTP: GET /local => LocalFunctionHandler |
GET /static | X-Handled-By: HTTP: GET /static => StaticMethodHandler |
GET /instance | X-Handled-By: HTTP: GET /instance => InstanceMethodHandler |
GET /home/index | X-Handled-By: MyWebApplication.Controllers.HomeController.Index (MyAspNetMvcApplication) |
Se puede ver que cuando el manejador es una acción MVC, el encabezado muestra el nombre del proyecto, controlador y acción que ejecutó la petición. Por tanto, podemos saber directamente dónde se encuentra su código.
En el caso de los endpoints es más complicado, porque DisplayName
no ofrece más información, pero es más que suficiente para saber desde dónde se gestionan. En todo caso, al aparecer el verbo y la ruta sabremos que es un mapeo, y en caso de estar delegado a métodos o funciones locales tendremos conoceremos cuáles son.
Publicado en Variable not found.
Aún no hay comentarios, ¡sé el primero!
Enviar un nuevo comentario
Backlinks: