Перейти к содержанию

Создание специального компонента маршрутизации конечных точек

Как уже было сказано, маршрутизация конечных точек разделяет процесс на два этапа, реализуемых отдельными компонентами:

  • RoutingMiddleware — использует входящий запрос для выбора конечной точки. Предоставляет метаданные о выбранной конечной точке в HttpContext (например, требования к авторизации, задаваемые при помощи [Authorize]);
  • EndpointMiddleware — выполняет выбранную конечную точку для генерации ответа.

Преимущество двухэтапного процесса в том, что можно разместить промежуточное ПО между этапом выбора конечной точки и этапом выполнения.
Пусть мы хотим применить авторизацию к простой конечной точке ping-pong, построенной здесь. Для этого воспользуемся маршрутизацией конечных точек.
Сперва создадим класс PingPongMiddleware (согласно подходу отсюда):

public class PingPongMiddleware
{
    public PingPongMiddleware(RequestDelegate next) {}

    public async Task Invoke(HttpContext context)
    {
        context.Response.ContentType = "text/plain";
        await context.Response.WriteAsync("pong");
    }
}

Теперь преобразуем компонент в конечную точку. Для этого создадим мини-конвейер внутри лямбда-функции UseEndpoints(), используя метод CreateApplicationBuilder():
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        var endpoint = endpoints
            .CreateApplicationBuilder()
            .UseMiddleware<PingPongMiddleware>()
            .Build();

        endpoints.Map("/ping", endpoint);
        endpoints.MapRazorPages();
        endpoints.MapHealthChecks("/healthz");
    });
}

Совет

Заметьте, что Map() в IEndpointRouteBuilder создаёт новую конечную точку, ассоциированную с маршрутом. Эта функция концептуально отличается от описанной ранее, которая используется для ветвления конвейера.

Также целесообразно вынести создание конечной точки в отдельный метод расширения:

public static class EndpointRouteBuilderExtensions
{
    public static IEndpointConventionBuilder MapPingPong(this IEndpointRouteBuilder endpoints, string route)
    {
        var pipeline = endpoints
            .CreateApplicationBuilder()
            .UseMiddleware<PingPongMiddleware>()
            .Build();

        return endpoints
            .Map(route, pipeline)
            .WithDisplayName("Ping-pong");
    }
}

Теперь метод UseEndpoints() станет проще:
app.UseEndpoints(endpoints => 
{
    endpoints.MapPingPong("/ping");
    endpoints.MapRazorPages();
    endpoints.MapHealthChecks("/healthz");
});

Совет

Здесь мы использовали простой маршрут "/ping", но можно использовать шаблоны маршрута с параметрами, например "/ping/{count}". Подробности см. в блоге автора


Последнее обновление : 9 мая 2023 г.
Дата создания : 26 октября 2022 г.

Комментарии

Комментарии