Маршрутизация с помощью RouterMiddleware. Метод UseMvc

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7

Последнее обновление: 23.11.2019

Вместо использования конечных точек и EndpointMiddleware для созданию системы маршрутизации в приложении мы можем применять метод UseMvc(). Он встраивает в конвейер обработки запроса компонент RouterMiddleware, который используется для сопоставления запросов с маршрутами.

Стоит отметить, что мы можем использовать либо новую форму маршрутизации на основе конечных точек на основе EndpointRoutingMiddleware + EndpointMiddleware, либо старую форму маршрутизации с использованием RouterMiddleware. Поэтому если мы хотим использовать метод UseMvc(), то необходимо при подключении сервисов MVC в методе ConfigureServices явным образом отключить использование конечных точек. Вне зависимости от того, какой метод используется для подключения сервисов MVC, все эти методы в качестве параметра могут принимать делегат Action<MvcOptions>. И с помощью свойства EnableEndpointRouting объекта MvcOptions можно отключить использование конечных точек:

public void ConfigureServices(IServiceCollection services)
{
	services.AddControllersWithViews(mvcOtions=>
	{
		mvcOtions.EnableEndpointRouting = false;
	});
	
	// или так
	//services.AddControllers(mvcOtions =>
	//{
	//    mvcOtions.EnableEndpointRouting = false;
	//});
	// или так
	//services.AddMvcCore(mvcOtions =>
	//{
	//    mvcOtions.EnableEndpointRouting = false;
	//});
	// или так
	//services.AddMvc(mvcOtions =>
	//{
	//    mvcOtions.EnableEndpointRouting = false;
	//});
}

Применим метод UseMvc() для определения маршрутов:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
//using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace MvcRoutingApp
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews(mvcOtions=>
            {
                mvcOtions.EnableEndpointRouting = false;
            });
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseStaticFiles();

            // добавление компонентов mvc и определение маршрута
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

Обратите внимание, что теперь естественно не надо использовать методы UseRouting и UseEndpoints для встраивания в конвейер системы маршрутизации.

Метод app.UseMvc() в качестве параметра принимает объект Action<IRouteBuilder>, который добавляет один маршрут. Параметр "controller" будет сопоставляться по имени с одним из контроллеров приложения, а параметр "action" - с действием этого контроллера. Например, при запросе http://localhost:3456/Home/Index система выберет для обработки запроса контроллер Home - имя контроллера без префикса Controller и его действие Index.

Хотя данный способ является альтернативой использованию конечных точек, но в целом определение маршрутов будет выглядеть также, как и в методе UseEndpoints. Например, определим несколько маршрутов:

app.UseMvc(routes =>
{
    routes.MapRoute("api", "api/get", new { controller = "Home", action = "About" });

    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
});

Первый маршрут сопоставляется с запросом типа http://localhost:xxxx/api/get, и для обработки такого запроса будет выбираться метод About контроллера Home. Второй маршрут стандартный.

Поскольку второй маршрут является стандартным и применяется многими разработчиками, то для его создания определен специальный метод:

app.UseMvcWithDefaultRoute();

То есть этот вызов метода равносилен конструкции:

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
});

Поэтому вместо определения стандартного маршрута через app.UseMvc(), мы можем применять метод app.UseMvcWithDefaultRoute().

Анатомия метода UseMvc

Метод UseMvc имеет следующее определение:

public static IApplicationBuilder UseMvc(
            this IApplicationBuilder app,
            Action<IRouteBuilder> configureRoutes)
{
	if (app == null)
	{
		throw new ArgumentNullException(nameof(app));
	}

	if (configureRoutes == null)
	{
		throw new ArgumentNullException(nameof(configureRoutes));
	}

	VerifyMvcIsRegistered(app);

	var options = app.ApplicationServices.GetRequiredService<IOptions<MvcOptions>>();

	if (options.Value.EnableEndpointRouting)
	{
		var message =
			"Endpoint Routing does not support 'IApplicationBuilder.UseMvc(...)'. To use " +
			"'IApplicationBuilder.UseMvc' set 'MvcOptions.EnableEndpointRouting = false' inside " +
			"'ConfigureServices(...).";
			throw new InvalidOperationException(message);
	}

	var routes = new RouteBuilder(app)
	{
		DefaultHandler = app.ApplicationServices.GetRequiredService<MvcRouteHandler>(),
	};

	configureRoutes(routes);

	routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(app.ApplicationServices));

	return app.UseRouter(routes.Build());
}

Исходный код можно найти на гитхабе

Как можно увидеть, данный метод полагается на компонент RouterMiddleware, который был рассмотрен в статье RouteMiddlevare. Для создания маршрута применяетс класс RouteBuilder, а для обработки маршрутов фреймворк предоставляет встроенный класс MvcRouteHandler, исходный код которого можно найти здесь.

Применение своего обработчика маршрутов

Хотя по умолчанию все маршруты в UseMvc обрабатываются с помощью MvcRouteHandler, но его применение необязательно, и мы можем задать свою логику обработки маршрутов. Например, изменим класс Startup следующим образом:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Routing;

namespace MvcRoutingApp
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews(mvcOtions=>
            {
                mvcOtions.EnableEndpointRouting = false;
            });
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseStaticFiles();

            app.UseMvc(routes =>
            {
                routes.MapRoute("api/get", async context =>
                {
                    await context.Response.WriteAsync("для обработки использован маршрут api/get");
                });

                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

Для первого маршрута с шаблоном "api/get" здесь задан свой обработчик в виде асинхронного делегата, который отправляет в выходной поток одну строку. Остальные маршруты будут обрабатываться стандартным MvcRouteHandler.

Маршруты в ASP.NET Core MVC
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850