В чем разница между UseRouting и UseEndpoints в конвейере middleware ASP.NET Core?

Ответ

UseRouting и UseEndpoints — это два отдельных, но тесно связанных middleware в конвейере обработки запроса ASP.NET Core. Они разделяют процесс маршрутизации на две фазы.

app.UseRouting() — это промежуточное ПО для сопоставления маршрутов (route matching).

  • Оно вычисляет, какой контроллер и действие (или минимальный API endpoint) должны обработать входящий HTTP-запрос, на основе его пути и метода.
  • Не выполняет конечную обработку запроса. Его задача — определить маршрут и прикрепить к контексту запроса (HttpContext) метаданные (например, выбранный Endpoint).
  • После UseRouting вы можете использовать middleware, которым нужна информация о маршруте (например, app.UseAuthorization()).

app.UseEndpoints() — это промежуточное ПО для выполнения конечной точки (endpoint execution).

  • Оно принимает решение, сделанное UseRouting, и вызывает соответствующий обработчик (метод контроллера, делегат минимального API, Razor Page и т.д.).
  • Внутри UseEndpoints вы настраиваете сами конечные точки с помощью методов MapGet, MapControllers, MapRazorPages.

Типичная последовательность в Startup.Configure:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // ... Другое middleware (например, для обработки ошибок, статических файлов)

    app.UseRouting(); // Фаза 1: Сопоставление маршрута

    app.UseAuthentication(); // Может использовать информацию о маршруте
    app.UseAuthorization();  // Может использовать информацию о маршруте

    app.UseEndpoints(endpoints => // Фаза 2: Выполнение конечной точки
    {
        endpoints.MapControllers(); // Регистрация маршрутов для контроллеров
        endpoints.MapGet("/hello", async context =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    });
}

Итог: UseRouting — «мы решили, куда идти», UseEndpoints — «мы пошли туда и выполнили код».

Ответ 18+ 🔞

Давай разжую эту тему про UseRouting и UseEndpoints, чтобы ты понял, как там под капотом всё ебётся.

Представь себе почтальона Печкина, который принёс тебе письмо. Так вот, UseRouting — это когда он смотрит на конверт и думает: «Ага, квартира 44, семья Сидоровых, тема — жалоба на шум». Он определил адресата, но письмо в руки не отдал. Просто приклеил на лоб записку «Сидоровым, прочитать».

А UseEndpoints — это уже сам момент, когда дверь открывается, Сидоровы берут конверт, вскрывают и начинают орать: «Опять эти соседи сверху с перфоратором!». Это и есть выполнение, сам вызов твоего кода.

Короче, по полочкам:

  • app.UseRouting()СОПОСТАВЛЕНИЕ МАРШРУТА. Этот кусок кода — как умный сортировщик на почте. Он берёт входящий запрос (URL, метод), прогоняет его по всем зарегистрированным шаблонам (типа /api/users/{id}) и находит подходящий. Нашёл — и прикрепляет к запросу метку «Эй, следующий за мной, вот endpoint, который будет это обрабатывать». Сам он ничего не выполняет, только выбирает. Как будто говорит: «Окей, запрос на /api/users/5 GET — это будет метод GetUserById в UsersController».

  • app.UseEndpoints()ВЫПОЛНЕНИЕ КОНЕЧНОЙ ТОЧКИ. А вот это уже непосредственный вызов. Тот самый метод GetUserById(5) запускается, лезет в базу, формирует ответ и отдаёт его. Внутри UseEndpoints ты как раз и говоришь, какие есть endpoint'ы: контроллеры, Razor Pages или вот эти минимальные API-делегаты.

Зачем их разделили, эти два этапа? А затем, что между «выбрать, кто будет обрабатывать» и «обработать» нужно иногда вставить какую-нибудь промежуточную хуйню. Например, авторизацию.

Смотри, классическая раскладка в Startup.Configure:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // ... тут всякая общая хуета: обработка ошибок, статические файлы

    app.UseRouting(); // Фаза 1: Печкин определил адрес (Сидоровы, кв.44)

    // А вот теперь, ЗНАЯ, КУДА ИДЁТ ЗАПРОС, можно проверить пропуск.
    app.UseAuthentication(); // Проверяет, кто там стучится (аутентификация)
    app.UseAuthorization();  // Проверяет, а имеет ли право этот человек стучать именно в ЭТУ дверь (авторизация)
    // Без UseRouting() эти штуки не знали бы, какие требования к доступу у endpoint'а Сидоровых.

    app.UseEndpoints(endpoints => // Фаза 2: Вскрытие конверта и выполнение
    {
        endpoints.MapControllers(); // Все маршруты из контроллеров
        endpoints.MapGet("/hello", async context => // Минимальный API
        {
            await context.Response.WriteAsync("Hello World!");
        });
    });
}

Итог, чтобы в голове отложилось: UseRouting«РЕШИЛИ, куда идти». Маршрутизатор. UseEndpoints«ПОШЛИ туда и СДЕЛАЛИ дело». Исполнитель.

Разделили для гибкости, чтобы между решением и действием можно было вставить логику, которой нужно знать — а к кому, собственно, запрос-то пришёл. Всё гениальное — просто, а когда непросто, то просто нужно знать, где что ебётся.