Маршрутизация к действиям контроллера в ASP.NET CoreRouting to controller actions in ASP.NET Core

Райан Nowak), Kirk Ларкини Рик Андерсон (By Ryan Nowak, Kirk Larkin, and Rick Anderson

Контроллеры ASP.NET Core используют по промежуточного слоя маршрутизации для сопоставления URL-адресов входящих запросов и сопоставления их с действиями.ASP.NET Core controllers use the Routing middleware to match the URLs of incoming requests and map them to actions. Шаблоны маршрутов:Routes templates:

  • Определяются в коде запуска или атрибутах.Are defined in startup code or attributes.
  • Описание способов сопоставления путей URL-адресов с действиями.Describe how URL paths are matched to actions.
  • Используются для создания URL-адресов для ссылок.Are used to generate URLs for links. Созданные ссылки обычно возвращаются в ответах.The generated links are typically returned in responses.

Действия либо направляются по соглашению , либо маршрутизируются по атрибуту.Actions are either conventionally-routed or attribute-routed. При размещении маршрута на контроллере или действии он пересылается по атрибуту.Placing a route on the controller or action makes it attribute-routed. Дополнительные сведения см. в разделе Смешанная маршрутизация.See Mixed routing for more information.

Этот документ:This document:

  • Объясняет взаимодействие между MVC и маршрутизацией.Explains the interactions between MVC and routing:
  • Относится к системе маршрутизации по умолчанию, добавленной в ASP.NET Core 3,0, называемую маршрутизацией конечных точек.Refers to the default routing system added in ASP.NET Core 3.0, called endpoint routing. В целях совместимости можно использовать контроллеры с предыдущей версией маршрутизации.It's possible to use controllers with the previous version of routing for compatibility purposes. Инструкции см. в руководству по миграции 2.2-3.0 .See the 2.2-3.0 migration guide for instructions. Справочные материалы по устаревшей системе маршрутизации см. в версии 2,2 этого документа .Refer to the 2.2 version of this document for reference material on the legacy routing system.

Настройка обычного маршрутаSet up conventional route

При использовании обычной маршрутизацииStartup.Configure обычно имеет код, аналогичный приведенному ниже.Startup.Configure typically has code similar to the following when using conventional routing:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
});

В вызове UseEndpointsMapControllerRoute используется для создания одного маршрута.Inside the call to UseEndpoints, MapControllerRoute is used to create a single route. Один маршрут называется defaultным маршрутом.The single route is named default route. Большинство приложений с контроллерами и представлениями используют шаблон маршрута, аналогичный маршруту default.Most apps with controllers and views use a route template similar to the default route. Интерфейсы API-интерфейсов для интерфейса остальных должны использовать маршрутизацию атрибутов.REST APIs should use attribute routing.

"{controller=Home}/{action=Index}/{id?}"шаблона маршрута:The route template "{controller=Home}/{action=Index}/{id?}":

  • Соответствует URL-пути, например /Products/Details/5Matches a URL path like /Products/Details/5
  • Извлекает значения маршрута { controller = Products, action = Details, id = 5 } путем маркировки пути.Extracts the route values { controller = Products, action = Details, id = 5 } by tokenizing the path. Извлечение значений маршрута приводит к совпадению, если у приложения есть контроллер с именем ProductsController и действие Details.The extraction of route values results in a match if the app has a controller named ProductsController and a Details action:
public class ProductsController : Controller
{
    public IActionResult Details(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Метод мидисплайраутеинфо включен в Пример загрузки и используется для вывода сведений о маршрутизации.The MyDisplayRouteInfo method is included in the sample download and is used to display routing information.

  • /Products/Details/5 модель привязывает значение id = 5, чтобы задать для 5параметра id./Products/Details/5 model binds the value of id = 5 to set the id parameter to 5. Дополнительные сведения см. в разделе Привязка модели .See Model Binding for more details.
  • {controller=Home} определяет Home как controllerпо умолчанию.{controller=Home} defines Home as the default controller.
  • {action=Index} определяет Index как actionпо умолчанию.{action=Index} defines Index as the default action.
  • ? символ в {id?} определяет id как необязательный.The ? character in {id?} defines id as optional.
  • Параметры маршрута по умолчанию и необязательные параметры необязательно должны присутствовать в пути URL-адреса для сопоставления.Default and optional route parameters don't need to be present in the URL path for a match. Подробное описание синтаксиса шаблона маршрута см. в разделе Справочник по шаблону маршрута.See Route Template Reference for a detailed description of route template syntax.
  • Соответствует URL-пути /.Matches the URL path /.
  • Создает значения маршрута { controller = Home, action = Index }.Produces the route values { controller = Home, action = Index }.
  • Метод мидисплайраутеинфо включен в Пример загрузки и используется для вывода сведений о маршрутизации.The MyDisplayRouteInfo method is included in the sample download and is used to display routing information.

Значения для controller и action используют значения по умолчанию.The values for controller and action make use of the default values. id не создает значение, так как в URL-пути нет соответствующего сегмента.id doesn't produce a value since there's no corresponding segment in the URL path. / соответствует, только если существует HomeController и Index действие:/ only matches if there exists a HomeController and Index action:

public class HomeController : Controller
{
  public IActionResult Index() { ... }
}

Используя предыдущее определение контроллера и шаблон маршрута, HomeController.Index действие выполняется для следующих URL-путей:Using the preceding controller definition and route template, the HomeController.Index action is run for the following URL paths:

  • /Home/Index/17
  • /Home/Index
  • /Home
  • /

URL-путь / использует контроллеры Home и Index действие шаблона маршрута по умолчанию.The URL path / uses the route template default Home controllers and Index action. /Home URL-пути использует действие Index шаблона маршрута по умолчанию.The URL path /Home uses the route template default Index action.

Универсальный метод MapDefaultControllerRoute:The convenience method MapDefaultControllerRoute:

endpoints.MapDefaultControllerRoute();

МеняющейReplaces:

endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");

Маршрутизация настраивается с использованием UseRouting и UseEndpoints по промежуточного слоя.Routing is configured using the UseRouting and UseEndpoints middleware. Использование контроллеров:To use controllers:

Маршрутизация на основе соглашенийConventional routing

Стандартная маршрутизация используется с контроллерами и представлениями.Conventional routing is used with controllers and views. Маршрут default:The default route:

endpoints.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

является примером маршрутизации на основе соглашений.is an example of a conventional routing. Он называется обычной маршрутизацией , так как он устанавливает соглашение для URL-путей:It's called conventional routing because it establishes a convention for URL paths:

  • Первый сегмент пути, {controller=Home}, сопоставляется с именем контроллера.The first path segment, {controller=Home}, maps to the controller name.
  • Второй сегмент, {action=Index}, сопоставляется с именем действия .The second segment, {action=Index}, maps to the action name.
  • Третий сегмент, {id?} используется для необязательного id.The third segment, {id?} is used for an optional id. ? в {id?} делает его необязательным.The ? in {id?} makes it optional. id используется для соотнесения с сущностью модели.id is used to map to a model entity.

Используя этот default маршрут, путь URL-адреса:Using this default route, the URL path:

  • /Products/List сопоставляется ProductsController.List действию./Products/List maps to the ProductsController.List action.
  • /Blog/Article/17 сопоставляется с BlogController.Article и модель, как правило, привязывает параметр id к 17./Blog/Article/17 maps to BlogController.Article and typically model binds the id parameter to 17.

Это сопоставление:This mapping:

  • Основаны толькона именах контроллера и действий .Is based on the controller and action names only.
  • Не основано на пространствах имен, расположениях исходных файлов или параметров метода.Isn't based on namespaces, source file locations, or method parameters.

Использование обычной маршрутизации с маршрутом по умолчанию позволяет создавать приложения без необходимости создания нового шаблона URL-адреса для каждого действия.Using conventional routing with the default route allows creating the app without having to come up with a new URL pattern for each action. Для приложения с действиями в стиле CRUD с согласованностью URL-адресов на контроллерах:For an app with CRUD style actions, having consistency for the URLs across controllers:

  • Помогает упростить код.Helps simplify the code.
  • Делает пользовательский интерфейс более предсказуемым.Makes the UI more predictable.

Предупреждение

id в приведенном выше коде определяется шаблоном маршрута как необязательный.The id in the preceding code is defined as optional by the route template. Действия могут выполняться без дополнительного идентификатора, предоставленного в качестве части URL-адреса.Actions can execute without the optional ID provided as part of the URL. Обычно при пропускеid из URL-адреса:Generally, whenid is omitted from the URL:

  • id устанавливается в 0 с помощью привязки модели.id is set to 0 by model binding.
  • Сущность не найдена в базе данных, соответствующей id == 0.No entity is found in the database matching id == 0.

Маршрутизация атрибутов обеспечивает детальный контроль, чтобы сделать идентификатор необходимым для некоторых действий, а не для других.Attribute routing provides fine-grained control to make the ID required for some actions and not for others. По соглашению документация включает необязательные параметры, такие как id, когда они, скорее всего, будут отображаться в правильном использовании.By convention, the documentation includes optional parameters like id when they're likely to appear in correct usage.

Для большинства приложений следует выбрать базовую описательную схему маршрутизации таким образом, чтобы URL-адреса были удобочитаемыми и осмысленными.Most apps should choose a basic and descriptive routing scheme so that URLs are readable and meaningful. Традиционный маршрут по умолчанию {controller=Home}/{action=Index}/{id?}.The default conventional route {controller=Home}/{action=Index}/{id?}:

  • Поддерживает основную и описательную схемы маршрутизации.Supports a basic and descriptive routing scheme.
  • Является отправной точкой для приложений на базе пользовательского интерфейса.Is a useful starting point for UI-based apps.
  • — Единственный шаблон маршрута, необходимый для многих приложений пользовательского веб-интерфейса.Is the only route template needed for many web UI apps. Для больших веб-приложений пользовательского интерфейса другой маршрут, использующий области , если это необходимо.For larger web UI apps, another route using Areas if frequently all that's needed.

MapControllerRoute и MapAreaRoute:MapControllerRoute and MapAreaRoute :

  • Автоматически назначить значение заказа своим конечным точкам в соответствии с порядком их вызова.Automatically assign an order value to their endpoints based on the order they are invoked.

Маршрутизация конечных точек в ASP.NET Core 3,0 и более поздних версий:Endpoint routing in ASP.NET Core 3.0 and later:

  • Не имеет концепции маршрутов.Doesn't have a concept of routes.
  • Не предоставляет гарантий упорядочения для выполнения расширяемости, все конечные точки обрабатываются одновременно.Doesn't provide ordering guarantees for the execution of extensibility, all endpoints are processed at once.

Чтобы увидеть, как встроенные реализации маршрутизации, такие как , сопоставляются с запросами, включите ведение журналаRoute.Enable Logging to see how the built-in routing implementations, such as Route, match requests.

Маршрутизация атрибутов объясняется далее в этом документе.Attribute routing is explained later in this document.

Несколько обычных маршрутовMultiple conventional routes

В UseEndpoints можно добавить несколько обычных маршрутов , добавив дополнительные вызовы MapControllerRoute и MapAreaControllerRoute.Multiple conventional routes can be added inside UseEndpoints by adding more calls to MapControllerRoute and MapAreaControllerRoute. Это позволяет определить несколько соглашений или добавить традиционные маршруты, предназначенные для конкретного действия, например:Doing so allows defining multiple conventions, or to adding conventional routes that are dedicated to a specific action, such as:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(name: "blog",
                pattern: "blog/{*article}",
                defaults: new { controller = "Blog", action = "Article" });
    endpoints.MapControllerRoute(name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
});

blog маршрут в приведенном выше коде является выделенным обычным маршрутом.The blog route in the preceding code is a dedicated conventional route. Он называется выделенным обычным маршрутом по следующим причинам.It's called a dedicated conventional route because:

Так как controller и action не отображаются в шаблоне маршрута "blog/{*article}" в качестве параметров:Because controller and action don't appear in the route template "blog/{*article}" as parameters:

  • Они могут иметь только значения по умолчанию { controller = "Blog", action = "Article" }.They can only have the default values { controller = "Blog", action = "Article" }.
  • Этот маршрут всегда сопоставляется с действием BlogController.Article.This route always maps to the action BlogController.Article.

/Blog, /Blog/Articleи /Blog/{any-string} являются единственными URL-путями, соответствующими маршруту блога./Blog, /Blog/Article, and /Blog/{any-string} are the only URL paths that match the blog route.

В предыдущем примере:The preceding example:

  • blog маршрут имеет более высокий приоритет для совпадений, чем маршрут default, так как он добавляется первым.blog route has a higher priority for matches than the default route because it is added first.
  • — И пример маршрутизации по стилю , в которой в качестве части URL-адреса обычно используется имя статьи.Is and example of Slug style routing where it's typical to have an article name as part of the URL.

Предупреждение

В ASP.NET Core 3,0 и более поздних версиях маршрутизация не выполняет:In ASP.NET Core 3.0 and later, routing doesn't:

  • Определите концепцию, называемую маршрутом.Define a concept called a route. UseRouting добавляет сопоставление маршрутов в конвейер по промежуточного слоя.UseRouting adds route matching to the middleware pipeline. По промежуточного слоя UseRouting просматривает набор конечных точек, определенных в приложении, и выбирает наилучшее совпадение конечных точек на основе запроса.The UseRouting middleware looks at the set of endpoints defined in the app, and selects the best endpoint match based on the request.
  • Предоставьте гарантии порядка выполнения расширяемости, например IRouteConstraint или IActionConstraint.Provide guarantees about the execution order of extensibility like IRouteConstraint or IActionConstraint.

См. раздел о маршрутизации для справочных материалов по маршрутизации.See Routing for reference material on routing.

Стандартный порядок маршрутизацииConventional routing order

Обычная маршрутизация соответствует только комбинации действий и контроллера, определенных приложением.Conventional routing only matches a combination of action and controller that are defined by the app. Это предназначено для упрощения случаев, когда обычные маршруты перекрываются.This is intended to simplify cases where conventional routes overlap. Добавление маршрутов с помощью MapControllerRoute, MapDefaultControllerRouteи MapAreaControllerRoute автоматически присваивает значение порядка их конечным точкам в соответствии с порядком их вызова.Adding routes using MapControllerRoute, MapDefaultControllerRoute, and MapAreaControllerRoute automatically assign an order value to their endpoints based on the order they are invoked. Совпадает с более высоким приоритетом на маршруте, который отображается ранее.Matches from a route that appears earlier have a higher priority. При маршрутизации на основе соглашений учитывается порядок.Conventional routing is order-dependent. Как правило, маршруты с областями следует размещать раньше, так как они более специфичны, чем маршруты без области.In general, routes with areas should be placed earlier as they're more specific than routes without an area. Выделенные обычные маршруты с перехватом всех параметров маршрута, таких как {*article}, могут сделать маршрут слишком жадным, то есть он будет соответствовать URL-адресам, которые должны сопоставляться другими маршрутами.Dedicated conventional routes with catch all route parameters like {*article} can make a route too greedy, meaning that it matches URLs that you intended to be matched by other routes. Помещайте жадные маршруты позже в таблице маршрутов, чтобы предотвратить жадные соответствия.Put the greedy routes later in the route table to prevent greedy matches.

Разрешение неоднозначных действийResolving ambiguous actions

При совпадении двух конечных точек через маршрутизацию маршрутизация должна выполнить одно из следующих действий:When two endpoints match through routing, routing must do one of the following:

  • Выберите лучший кандидат.Choose the best candidate.
  • Создание исключения.Throw an exception.

Например:For example:

    public class Products33Controller : Controller
    {
        public IActionResult Edit(int id)
        {
            return ControllerContext.MyDisplayRouteInfo(id);
        }

        [HttpPost]
        public IActionResult Edit(int id, Product product)
        {
            return ControllerContext.MyDisplayRouteInfo(id, product.name);
        }
    }
}

Предыдущий контроллер определяет два соответствующих действия:The preceding controller defines two actions that match:

  • URL-путь /Products33/Edit/17The URL path /Products33/Edit/17
  • { controller = Products33, action = Edit, id = 17 }данных маршрута.Route data { controller = Products33, action = Edit, id = 17 }.

Это типичный шаблон для контроллеров MVC:This is a typical pattern for MVC controllers:

  • Edit(int) отображает форму для изменения продукта.Edit(int) displays a form to edit a product.
  • Edit(int, Product) обрабатывает опубликованную форму.Edit(int, Product) processes the posted form.

Чтобы разрешить правильный маршрут, выполните следующие действия.To resolve the correct route:

  • Edit(int, Product) выбирается, когда запрос является HTTP-POST.Edit(int, Product) is selected when the request is an HTTP POST.
  • Edit(int) выбирается, когда команда HTTP является любым другим.Edit(int) is selected when the HTTP verb is anything else. Edit(int) обычно вызывается с помощью GET.Edit(int) is generally called via GET.

HttpPostAttribute, [HttpPost], предоставляется для маршрутизации, чтобы ее можно было выбрать в зависимости от метода HTTP запроса.The HttpPostAttribute, [HttpPost], is provided to routing so that it can choose based on the HTTP method of the request. HttpPostAttribute делает Edit(int, Product) более подходящие, чем Edit(int).The HttpPostAttribute makes Edit(int, Product) a better match than Edit(int).

Важно понимать роль таких атрибутов, как HttpPostAttribute.It's important to understand the role of attributes like HttpPostAttribute. Аналогичные атрибуты определяются для других HTTP-команд.Similar attributes are defined for other HTTP verbs. В обычной маршрутизациидля действий используется одно и то же имя действия, когда они входят в форму отображения, Рабочий процесс отправки формы.In conventional routing, it's common for actions to use the same action name when they're part of a show form, submit form workflow. Например, см. раздел изучение двух методов действия Edit.For example, see Examine the two Edit action methods.

Если службе маршрутизации не удается выбрать лучший вариант, выдается AmbiguousMatchException, где перечисляются несколько совпадающих конечных точек.If routing can't choose a best candidate, an AmbiguousMatchException is thrown, listing the multiple matched endpoints.

Имена обычных маршрутовConventional route names

Строки "blog" и "default" в следующих примерах являются стандартными именами маршрутов:The strings "blog" and "default" in the following examples are conventional route names:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(name: "blog",
                pattern: "blog/{*article}",
                defaults: new { controller = "Blog", action = "Article" });
    endpoints.MapControllerRoute(name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
});

Имена маршрутов придают маршруту логическое имя.The route names give the route a logical name. Именованный маршрут можно использовать для создания URL-адресов.The named route can be used for URL generation. Использование именованного маршрута упрощает создание URL-адресов, когда порядок маршрутов может усложнить создание URL-адреса.Using a named route simplifies URL creation when the ordering of routes could make URL generation complicated. Имена маршрутов должны быть уникальными для всего приложения.Route names must be unique application wide.

Имена маршрутов:Route names:

  • Не влияют на сопоставление URL-адресов или обработку запросов.Have no impact on URL matching or handling of requests.
  • Используются только для создания URL-адресов.Are used only for URL generation.

Концепция имени маршрута представлена в маршрутизации как иендпоинтнамеметадата.The route name concept is represented in routing as IEndpointNameMetadata. Термины имя маршрута и имя конечной точки:The terms route name and endpoint name:

  • Являются взаимозаменяемыми.Are interchangeable.
  • То, какой из них используется в документации и коде, зависит от описываемого API.Which one is used in documentation and code depends on the API being described.

Маршрутизация атрибутов для интерфейсов API RESTFULAttribute routing for REST APIs

Для моделирования функциональных возможностей приложения в качестве набора ресурсов, в которых операции представлены HTTP-командами, интерфейсы API-интерфейса должны использовать маршрутизацию атрибутов.REST APIs should use attribute routing to model the app's functionality as a set of resources where operations are represented by HTTP verbs.

При маршрутизации с помощью атрибутов используется набор атрибутов для сопоставления действий непосредственно с шаблонами маршрутов.Attribute routing uses a set of attributes to map actions directly to route templates. Следующий StartUp.Configure код типичен для REST API и используется в следующем примере:The following StartUp.Configure code is typical for a REST API and is used in the next sample:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
}

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

    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

В приведенном выше коде MapControllers вызывается в UseEndpoints для подключения к перенаправленным контроллерам атрибута.In the preceding code, MapControllers is called inside UseEndpoints to map attribute routed controllers.

Рассмотрим следующий пример:In the following example:

  • Используется предыдущий метод Configure.The preceding Configure method is used.
  • HomeController соответствует набору URL-адресов, аналогично тому, который {controller=Home}/{action=Index}/{id?} совпадает с обычным маршрутом по умолчанию.HomeController matches a set of URLs similar to what the default conventional route {controller=Home}/{action=Index}/{id?} matches.
public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult Index(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult About(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

HomeController.Index действие выполняется для любого из URL-путей /, /Home, /Home/Indexили /Home/Index/3.The HomeController.Index action is run for any of the URL paths /, /Home, /Home/Index, or /Home/Index/3.

В этом примере демонстрируется ключевое различие в программировании между маршрутизацией атрибутов и обычной маршрутизацией.This example highlights a key programming difference between attribute routing and conventional routing. Маршрутизация атрибутов требует больше входных данных для указания маршрута.Attribute routing requires more input to specify a route. Стандартный маршрут по умолчанию обрабатывает маршруты более кратко.The conventional default route handles routes more succinctly. Однако маршрутизация атрибутов разрешает и требует точного контроля над тем, какие шаблоны маршрутов применяются к каждому действию.However, attribute routing allows and requires precise control of which route templates apply to each action.

При использовании маршрутизации атрибутов имя контроллера и имена действий не воспроизводят роль, в которой сопоставляется действие.With attribute routing, the controller name and action names play no role in which action is matched. В следующем примере совпадают те же URL-адреса, что и в предыдущем примере:The following example matches the same URLs as the previous example:

public class MyDemoController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult MyIndex(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult MyAbout(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

В следующем коде используется замена маркера для action и controller:The following code uses token replacement for action and controller:

public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("[controller]/[action]")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [Route("[controller]/[action]")]
    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Следующий код применяется [Route("[controller]/[action]")] к контроллеру:The following code applies [Route("[controller]/[action]")] to the controller:

public class HomeController : Controller
{
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

В приведенном выше коде шаблоны методов Index должны в начале / или ~/ шаблоны маршрутов.In the preceding code, the Index method templates must prepend / or ~/ to the route templates. Шаблоны маршрутов, применяемые к действию, которое начинается с символа / или ~/, не объединяются с шаблонами маршрутов, применяемыми к контроллеру.Route templates applied to an action that begin with / or ~/ don't get combined with route templates applied to the controller.

Сведения о выборе шаблона маршрутов см. в разделе приоритет шаблона маршрута .See Route template precedence for information on route template selection.

Зарезервированные имена маршрутизацииReserved routing names

При использовании контроллеров или Razor Pages следующие ключевые слова являются зарезервированными именами параметров маршрута:The following keywords are reserved route parameter names when using Controllers or Razor Pages:

  • action
  • area
  • controller
  • handler
  • page

Использование page в качестве параметра маршрута с маршрутизацией атрибутов является распространенной ошибкой.Using page as a route parameter with attribute routing is a common error. Это приводит к несогласованности и путанице при формировании URL-адреса.Doing that results in inconsistent and confusing behavior with URL generation.

public class MyDemo2Controller : Controller
    {
    [Route("/articles/{page}")]
    public IActionResult ListArticles(int page)
    {
        return ControllerContext.MyDisplayRouteInfo(page);
    }
}

Специальные имена параметров используются при формировании URL-адресов для определения того, относится ли операция формирования URL-адреса к странице Razor или к контроллеру.The special parameter names are used by the URL generation to determine if a URL generation operation refers to a Razor Page or to a Controller.

Шаблоны HTTP-командHTTP verb templates

ASP.NET Core содержит следующие шаблоны HTTP-команд:ASP.NET Core has the following HTTP verb templates:

Шаблоны маршрутовRoute templates

ASP.NET Core имеет следующие шаблоны маршрутов:ASP.NET Core has the following route templates:

Маршрутизация атрибутов с помощью атрибутов глагола HTTPAttribute routing with Http verb attributes

Рассмотрим следующий контроллер:Consider the following controller:

[Route("api/[controller]")]
[ApiController]
public class Test2Controller : ControllerBase
{
    [HttpGet]   // GET /api/test2
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpGet("int/{id:int}")] // GET /api/test2/int/3
    public IActionResult GetIntProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpGet("int2/{id}")]  // GET /api/test2/int2/3
    public IActionResult GetInt2Product(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

В приведенном выше коде:In the preceding code:

  • Каждое действие содержит атрибут [HttpGet], который ограничивает сопоставление только запросами HTTP GET.Each action contains the [HttpGet] attribute, which constrains matching to HTTP GET requests only.
  • GetProduct действие включает шаблон "{id}", поэтому id добавляется к шаблону "api/[controller]" на контроллере.The GetProduct action includes the "{id}" template, therefore id is appended to the "api/[controller]" template on the controller. Шаблон методов "api/[controller]/"{id}"".The methods template is "api/[controller]/"{id}"". Поэтому это действие соответствует только запросам GET для формы /api/test2/xyz,/api/test2/123,/api/test2/{any string}и т. д.Therefore this action only matches GET requests of for the form /api/test2/xyz,/api/test2/123,/api/test2/{any string}, etc.
    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }
    
  • Действие GetIntProduct содержит шаблон "int/{id:int}").The GetIntProduct action contains the "int/{id:int}") template. :int часть шаблона ограничивает id значения маршрута строками, которые можно преобразовать в целое число.The :int portion of the template constrains the id route values to strings that can be converted to an integer. Запрос GET к /api/test2/int/abc:A GET request to /api/test2/int/abc:
    • Не соответствует этому действию.Doesn't match this action.
    • Возвращает ошибку 404 не найден .Returns a 404 Not Found error.
      [HttpGet("int/{id:int}")] // GET /api/test2/int/3
      public IActionResult GetIntProduct(int id)
      {
          return ControllerContext.MyDisplayRouteInfo(id);
      }
      
  • Действие GetInt2Product содержит {id} в шаблоне, но не ограничивает id значениями, которые можно преобразовать в целое число.The GetInt2Product action contains {id} in the template, but doesn't constrain id to values that can be converted to an integer. Запрос GET к /api/test2/int2/abc:A GET request to /api/test2/int2/abc:
    • Соответствует этому маршруту.Matches this route.
    • Привязке модели не удается преобразовать abc в целое число.Model binding fails to convert abc to an integer. Параметр id метода имеет тип Integer.The id parameter of the method is integer.
    • Возвращает 400 Неверный запрос , так как привязка модели не смогла преобразоватьabc в целое число.Returns a 400 Bad Request because model binding failed to convertabc to an integer.
      [HttpGet("int2/{id}")]  // GET /api/test2/int2/3
      public IActionResult GetInt2Product(int id)
      {
          return ControllerContext.MyDisplayRouteInfo(id);
      }
      

Маршрутизация атрибутов может использовать HttpMethodAttribute атрибуты, такие как HttpPostAttribute, HttpPutAttributeи HttpDeleteAttribute.Attribute routing can use HttpMethodAttribute attributes such as HttpPostAttribute, HttpPutAttribute, and HttpDeleteAttribute. Все атрибуты HTTP-команды принимают шаблон маршрута.All of the HTTP verb attributes accept a route template. В следующем примере показаны два действия, которые соответствуют одному шаблону маршрута:The following example shows two actions that match the same route template:

[ApiController]
public class MyProductsController : ControllerBase
{
    [HttpGet("/products3")]
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpPost("/products3")]
    public IActionResult CreateProduct(MyProduct myProduct)
    {
        return ControllerContext.MyDisplayRouteInfo(myProduct.Name);
    }
}

Использование URL-пути /products3:Using the URL path /products3:

  • Действие MyProductsController.ListProducts выполняется, когда HTTP-команда GET.The MyProductsController.ListProducts action runs when the HTTP verb is GET.
  • Действие MyProductsController.CreateProduct выполняется, когда HTTP-команда POST.The MyProductsController.CreateProduct action runs when the HTTP verb is POST.

При создании REST API в редких случаях необходимо использовать [Route(...)] в методе действия, поскольку действие принимает все методы HTTP.When building a REST API, it's rare that you'll need to use [Route(...)] on an action method because the action accepts all HTTP methods. Лучше использовать более конкретный атрибут HTTP-команды , чтобы точно определить, что поддерживает API.It's better to use the more specific HTTP verb attribute to be precise about what your API supports. Клиенты интерфейсов REST API должны знать, какие пути и HTTP-команды сопоставляются с определенными логическими операциями.Clients of REST APIs are expected to know what paths and HTTP verbs map to specific logical operations.

Для моделирования функциональных возможностей приложения в качестве набора ресурсов, в которых операции представлены HTTP-командами, интерфейсы API-интерфейса должны использовать маршрутизацию атрибутов.REST APIs should use attribute routing to model the app's functionality as a set of resources where operations are represented by HTTP verbs. Это означает, что многие операции, например GET и POST для одного и того же логического ресурса, используют один и тот же URL-адрес.This means that many operations, for example, GET and POST on the same logical resource use the same URL. Маршрутизация с помощью атрибутов обеспечивает необходимый уровень контроля, позволяющий тщательно разработать схему общедоступных конечных точек API-интерфейса.Attribute routing provides a level of control that's needed to carefully design an API's public endpoint layout.

Так как маршрут на основе атрибутов применяется к определенному действию, можно легко сделать параметры обязательными в рамках определения шаблона маршрута.Since an attribute route applies to a specific action, it's easy to make parameters required as part of the route template definition. В следующем примере id требуется в качестве части URL-пути:In the following example, id is required as part of the URL path:

[ApiController]
public class Products2ApiController : ControllerBase
{
    [HttpGet("/products2/{id}", Name = "Products_List")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Действие Products2ApiController.GetProduct(int):The Products2ApiController.GetProduct(int) action:

  • Выполняется с URL-путем, например /products2/3Is run with URL path like /products2/3
  • Не выполняется с URL-адресом /products2.Isn't run with the URL path /products2.

Атрибут [Consumes] позволяет выполнять действие для ограничения поддерживаемых типов содержимого запросов.The [Consumes] attribute allows an action to limit the supported request content types. Дополнительные сведения см. в разделе Определение поддерживаемых типов содержимого запросов с помощью атрибута использования.For more information, see Define supported request content types with the Consumes attribute.

Полное описание шаблонов маршрутов и связанных параметров см. в статье Маршрутизация.See Routing for a full description of route templates and related options.

Дополнительные сведения о [ApiController]см. в разделе атрибут ApiController.For more information on [ApiController], see ApiController attribute.

Имя маршрутаRoute name

Следующий код определяет имя маршрута Products_List:The following code defines a route name of Products_List:

[ApiController]
public class Products2ApiController : ControllerBase
{
    [HttpGet("/products2/{id}", Name = "Products_List")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Имена маршрутов могут использоваться для формирования URL-адреса на основе определенного маршрута.Route names can be used to generate a URL based on a specific route. Имена маршрутов:Route names:

  • Не влияют на поведение маршрутизации в соответствии с URL-адресом.Have no impact on the URL matching behavior of routing.
  • Используются только для создания URL-адресов.Are only used for URL generation.

Имена маршрутов должны быть уникальными в пределах приложения.Route names must be unique application-wide.

Сравните предыдущий код с обычным маршрутом по умолчанию, который определяет id параметр как необязательный ({id?}).Contrast the preceding code with the conventional default route, which defines the id parameter as optional ({id?}). Возможность точного указания интерфейсов API имеет свои преимущества, такие как разрешение /products и /products/5 отправляются в различные действия.The ability to precisely specify APIs has advantages, such as allowing /products and /products/5 to be dispatched to different actions.

Объединение маршрутов атрибутовCombining attribute routes

Чтобы избежать лишних повторов при маршрутизации с помощью атрибутов, атрибуты маршрута для контроллера объединяются с атрибутами маршрута для отдельных действий.To make attribute routing less repetitive, route attributes on the controller are combined with route attributes on the individual actions. Все шаблоны маршрутов, определенные в контроллере, добавляются перед шаблонами маршрутов для действий.Any route templates defined on the controller are prepended to route templates on the actions. В результате добавления атрибута маршрута для контроллера все его действия будут использовать маршрутизацию с помощью атрибутов.Placing a route attribute on the controller makes all actions in the controller use attribute routing.

[ApiController]
[Route("products")]
public class ProductsApiController : ControllerBase
{
    [HttpGet]
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

В предшествующем примере:In the preceding example:

  • URL-путь /products может соответствовать ProductsApi.ListProductsThe URL path /products can match ProductsApi.ListProducts
  • URL-путь /products/5 может соответствовать ProductsApi.GetProduct(int).The URL path /products/5 can match ProductsApi.GetProduct(int).

Оба эти действия соответствуют только HTTP-GET, так как они помечены атрибутом [HttpGet].Both of these actions only match HTTP GET because they're marked with the [HttpGet] attribute.

Шаблоны маршрутов, применяемые к действию, которое начинается с символа / или ~/, не объединяются с шаблонами маршрутов, применяемыми к контроллеру.Route templates applied to an action that begin with / or ~/ don't get combined with route templates applied to the controller. Следующий пример соответствует набору URL-путей, аналогичному маршруту по умолчанию.The following example matches a set of URL paths similar to the default route.

[Route("Home")]
public class HomeController : Controller
{
    [Route("")]
    [Route("Index")]
    [Route("/")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [Route("About")]
    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

В следующей таблице описаны атрибуты [Route] в приведенном выше коде.The following table explains the [Route] attributes in the preceding code:

АтрибутAttribute Объединяет с [Route("Home")]Combines with [Route("Home")] Определение шаблона маршрутаDefines route template
[Route("")] ДаYes "Home"
[Route("Index")] ДаYes "Home/Index"
[Route("/")] НетNo ""
[Route("About")] ДаYes "Home/About"

Порядок маршрута атрибутаAttribute route order

Маршрутизация создает дерево и сопоставляет все конечные точки одновременно:Routing builds a tree and matches all endpoints simultaneously:

  • Записи маршрутов ведут себя так, как если бы они были размещены в идеальном порядке.The route entries behave as if placed in an ideal ordering.
  • Наиболее конкретные маршруты могут выполняться до более общих маршрутов.The most specific routes have a chance to execute before the more general routes.

Например, маршрут атрибута, подобный blog/search/{topic}, более специфичен, чем маршрут атрибута, такой как blog/{*article}.For example, an attribute route like blog/search/{topic} is more specific than an attribute route like blog/{*article}. По умолчанию маршрут blog/search/{topic} имеет более высокий приоритет, так как он является более конкретным.The blog/search/{topic} route has higher priority, by default, because it's more specific. При использовании обычной маршрутизацииразработчик несет ответственность за размещение маршрутов в нужном порядке.Using conventional routing, the developer is responsible for placing routes in the desired order.

Маршруты атрибутов могут настраивать порядок с помощью свойства Order.Attribute routes can configure an order using the Order property. Все указанные в платформе атрибуты маршрута включают Order.All of the framework provided route attributes include Order . Маршруты обрабатываются в порядке возрастания значения свойства Order.Routes are processed according to an ascending sort of the Order property. Порядок по умолчанию — 0.The default order is 0. Настройка маршрута с помощью Order = -1 выполняется перед маршрутами, которые не задают порядок.Setting a route using Order = -1 runs before routes that don't set an order. Настройка маршрута с помощью Order = 1 выполняется после упорядочения маршрутов по умолчанию.Setting a route using Order = 1 runs after default route ordering.

Избегайте в зависимости от Order.Avoid depending on Order. Если для правильной маршрутизации URL-адресу приложения требуются явные значения порядка, скорее всего, это приведет к путанице с клиентами.If an app's URL-space requires explicit order values to route correctly, then it's likely confusing to clients as well. Как правило, маршрутизация атрибутов выбирает правильный маршрут с сопоставлением URL-адресов.In general, attribute routing selects the correct route with URL matching. Если порядок по умолчанию, используемый для создания URL-адреса, не работает, использование имени маршрута в качестве переопределения обычно проще, чем применение свойства Order.If the default order used for URL generation isn't working, using a route name as an override is usually simpler than applying the Order property.

Рассмотрим следующие два контроллера, которые определяют /homeсопоставления маршрутов.Consider the following two controllers which both define the route matching /home:

public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult Index(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult About(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}
public class MyDemoController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult MyIndex(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult MyAbout(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

При запросе /home с помощью приведенного выше кода создается исключение, аналогичное следующему:Requesting /home with the preceding code throws an exception similar to the following:

AmbiguousMatchException: The request matched multiple endpoints. Matches:

 WebMvcRouting.Controllers.HomeController.Index
 WebMvcRouting.Controllers.MyDemoController.MyIndex

Добавление Order к одному из атрибутов маршрута разрешает неоднозначность:Adding Order to one of the route attributes resolves the ambiguity:

[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
    return ControllerContext.MyDisplayRouteInfo();
}

В приведенном выше коде /home запускает конечную точку HomeController.Index.With the preceding code, /home runs the HomeController.Index endpoint. Чтобы получить MyDemoController.MyIndex, запросите /home/MyIndex.To get to the MyDemoController.MyIndex, request /home/MyIndex. Примечание.Note:

  • Приведенный выше код представляет собой пример или низкую структуру маршрутизации.The preceding code is an example or poor routing design. Он использовался для иллюстрации свойства Order.It was used to illustrate the Order property.
  • Свойство Order разрешает неоднозначность, но этот шаблон не может быть сопоставлен.The Order property only resolves the ambiguity, that template cannot be matched. Лучше удалить шаблон [Route("Home")].It would be better to remove the [Route("Home")] template.

См. раздел соглашения о Razor Pages маршрутах и приложениях. порядок маршрута для получения сведений о порядке маршрутов с Razor Pages.See Razor Pages route and app conventions: Route order for information on route order with Razor Pages.

В некоторых случаях возвращается ошибка HTTP 500 с неоднозначными маршрутами.In some cases, an HTTP 500 error is returned with ambiguous routes. Используйте ведение журнала , чтобы узнать, какие конечные точки привели к возникновению AmbiguousMatchException.Use logging to see which endpoints caused the AmbiguousMatchException.

Замена токенов в шаблонах маршрутов [контроллер], [действие], [область]Token replacement in route templates [controller], [action], [area]

Для удобства маршруты к атрибутам поддерживают замену маркера для зарезервированных параметров маршрута путем заключения маркера в один из следующих элементов:For convenience, attribute routes support token replacement for reserved route parameters by enclosing a token in one of the following:

  • Квадратные скобки: []Square braces: []
  • Фигурные скобки: {}Curly braces: {}

Маркеры [action], [area]и [controller] заменяются значениями имени действия, имени области и имени контроллера из действия, в котором определен маршрут:The tokens [action], [area], and [controller] are replaced with the values of the action name, area name, and controller name from the action where the route is defined:

[Route("[controller]/[action]")]
public class Products0Controller : Controller
{
    [HttpGet]
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }


    [HttpGet("{id}")]
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

В приведенном выше коде:In the preceding code:

[HttpGet]
public IActionResult List()
{
    return ControllerContext.MyDisplayRouteInfo();
}
  • Соответствует /Products0/ListMatches /Products0/List
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
    return ControllerContext.MyDisplayRouteInfo(id);
}
  • Соответствует /Products0/Edit/{id}Matches /Products0/Edit/{id}

Замена токенов происходит на последнем этапе создания маршрутов на основе атрибутов.Token replacement occurs as the last step of building the attribute routes. Предыдущий пример ведет себя так же, как и следующий код:The preceding example behaves the same as the following code:

public class Products20Controller : Controller
{
    [HttpGet("[controller]/[action]")]  // Matches '/Products20/List'
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("[controller]/[action]/{id}")]   // Matches '/Products20/Edit/{id}'
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Если вы читаете это не на английском языке, сообщите нам в этом обсуждении на GitHub, хотели бы вы видеть комментарии к коду на своем родном языке.If you are reading this in a language other than English, let us know in this GitHub discussion issue if you’d like to see the code comments in your native language.

Маршруты на основе атрибутов могут также сочетаться с наследованием.Attribute routes can also be combined with inheritance. Это мощное сочетание с заменой токенов.This is powerful combined with token replacement. Замена токенов также применяется к именам маршрутов, определенным в маршрутах на основе атрибутов.Token replacement also applies to route names defined by attribute routes. [Route("[controller]/[action]", Name="[controller]_[action]")]создает уникальное имя маршрута для каждого действия:[Route("[controller]/[action]", Name="[controller]_[action]")]generates a unique route name for each action:

[ApiController]
[Route("api/[controller]/[action]", Name = "[controller]_[action]")]
public abstract class MyBase2Controller : ControllerBase
{
}

public class Products11Controller : MyBase2Controller
{
    [HttpGet]                      // /api/products11/
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]             //    /api/products11/edit/3
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

Замена токенов также применяется к именам маршрутов, определенным в маршрутах на основе атрибутов.Token replacement also applies to route names defined by attribute routes. [Route("[controller]/[action]", Name="[controller]_[action]")] формирует уникальное имя маршрута для каждого действия.generates a unique route name for each action.

Для сопоставления с литеральным разделителем замены токенов [ или ] его следует экранировать путем повтора символа ([[ или ]]).To match the literal token replacement delimiter [ or ], escape it by repeating the character ([[ or ]]).

Использование преобразователя параметров для настройки замены токеновUse a parameter transformer to customize token replacement

Замену токенов можно настроить, используя преобразователь параметров.Token replacement can be customized using a parameter transformer. Преобразователь параметров реализует IOutboundParameterTransformer и преобразует значения параметров.A parameter transformer implements IOutboundParameterTransformer and transforms the value of parameters. Например, настраиваемый SlugifyParameterTransformer параметра Transformer изменяет значение SubscriptionManagement маршрута на subscription-management:For example, a custom SlugifyParameterTransformer parameter transformer changes the SubscriptionManagement route value to subscription-management:

public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
    public string TransformOutbound(object value)
    {
        if (value == null) { return null; }

        // Slugify value
        return Regex.Replace(value.ToString(),
                             "([a-z])([A-Z])", "$1-$2").ToLowerInvariant();
    }
}

RouteTokenTransformerConvention является соглашением для модели приложения, которое:The RouteTokenTransformerConvention is an application model convention that:

  • Применяет преобразователь параметров ко всем маршрутам атрибута в приложении.Applies a parameter transformer to all attribute routes in an application.
  • Настраивает значения токена маршрут атрибута при замене.Customizes the attribute route token values as they are replaced.
public class SubscriptionManagementController : Controller
{
    [HttpGet("[controller]/[action]")]
    public IActionResult ListAll()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Предыдущий метод ListAll соответствует /subscription-management/list-all.The preceding ListAll method matches /subscription-management/list-all.

RouteTokenTransformerConvention регистрируется в качестве параметра в ConfigureServices.The RouteTokenTransformerConvention is registered as an option in ConfigureServices.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
    {
        options.Conventions.Add(new RouteTokenTransformerConvention(
                                     new SlugifyParameterTransformer()));
    });
}

Определение служебной версии см. в веб-документах MDN в служебной системе .See MDN web docs on Slug for the definition of Slug.

Несколько маршрутов атрибутовMultiple attribute routes

Маршрутизация с помощью атрибутов поддерживает определение нескольких маршрутов к одному и тому же действию.Attribute routing supports defining multiple routes that reach the same action. Наиболее распространенным применением этого способа является имитация поведения стандартного маршрута по умолчанию, как показано в следующем примере:The most common usage of this is to mimic the behavior of the default conventional route as shown in the following example:

[Route("[controller]")]
public class Products13Controller : Controller
{
    [Route("")]     // Matches 'Products13'
    [Route("Index")] // Matches 'Products13/Index'
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

Размещение нескольких атрибутов маршрута на контроллере означает, что каждый из них объединяется с каждым атрибутом маршрута в методах действия:Putting multiple route attributes on the controller means that each one combines with each of the route attributes on the action methods:

[Route("Store")]
[Route("[controller]")]
public class Products6Controller : Controller
{
    [HttpPost("Buy")]       // Matches 'Products6/Buy' and 'Store/Buy'
    [HttpPost("Checkout")]  // Matches 'Products6/Checkout' and 'Store/Checkout'
    public IActionResult Buy()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Все ограничения маршрута HTTP-команд реализуют IActionConstraint.All the HTTP verb route constraints implement IActionConstraint.

Если в действие помещаются несколько атрибутов маршрута, реализующих IActionConstraint:When multiple route attributes that implement IActionConstraint are placed on an action:

  • Каждое ограничение действия объединяется с шаблоном маршрута, примененным к контроллеру.Each action constraint combines with the route template applied to the controller.
[Route("api/[controller]")]
public class Products7Controller : ControllerBase
{
    [HttpPut("Buy")]        // Matches PUT 'api/Products7/Buy'
    [HttpPost("Checkout")]  // Matches POST 'api/Products7/Checkout'
    public IActionResult Buy()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Использование нескольких маршрутов в действиях может показаться полезным и мощным, поэтому лучше обеспечить базовое и четкое определение пространства URL-адресов вашего приложения.Using multiple routes on actions might seem useful and powerful, it's better to keep your app's URL space basic and well defined. Используйте несколько маршрутов в действиях , если это необходимо, например, для поддержки существующих клиентов.Use multiple routes on actions only where needed, for example, to support existing clients.

Задание необязательных параметров, значений по умолчанию и ограничений для маршрутов на основе атрибутовSpecifying attribute route optional parameters, default values, and constraints

Маршруты на основе атрибутов поддерживают тот же синтаксис для указания необязательных параметров, значений по умолчанию и ограничений, что и маршруты на основе соглашений.Attribute routes support the same inline syntax as conventional routes to specify optional parameters, default values, and constraints.

public class Products14Controller : Controller
{
    [HttpPost("product14/{id:int}")]
    public IActionResult ShowProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

В приведенном выше коде [HttpPost("product/{id:int}")] применяет ограничение маршрута.In the preceding code, [HttpPost("product/{id:int}")] applies a route constraint. ProductsController.ShowProduct действие сопоставляется только с URL-путями, такими как /product/3.The ProductsController.ShowProduct action is matched only by URL paths like /product/3. Часть шаблона маршрута {id:int} ограничивает этот сегмент только целыми числами.The route template portion {id:int} constrains that segment to only integers.

public class HomeController : Controller
{
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Подробное описание синтаксиса шаблона маршрута см. в разделе Справочник по шаблону маршрута.See Route Template Reference for a detailed description of route template syntax.

Настраиваемые атрибуты маршрута с помощью ИраутетемплатепровидерCustom route attributes using IRouteTemplateProvider

Все атрибуты маршрута реализуют IRouteTemplateProvider.All of the route attributes implement IRouteTemplateProvider. Среда выполнения ASP.NET Core:The ASP.NET Core runtime:

  • Ищет атрибуты классов контроллеров и методов действий при запуске приложения.Looks for attributes on controller classes and action methods when the app starts.
  • Использует атрибуты, реализующие IRouteTemplateProvider, для построения начального набора маршрутов.Uses the attributes that implement IRouteTemplateProvider to build the initial set of routes.

Реализуйте IRouteTemplateProvider для определения пользовательских атрибутов маршрута.Implement IRouteTemplateProvider to define custom route attributes. Каждая реализация IRouteTemplateProvider позволяет определить один маршрут с пользовательским шаблоном маршрута, порядком и именем.Each IRouteTemplateProvider allows you to define a single route with a custom route template, order, and name:

public class MyApiControllerAttribute : Attribute, IRouteTemplateProvider
{
    public string Template => "api/[controller]";
    public int? Order => 2;
    public string Name { get; set; }
}

[MyApiController]
[ApiController]
public class MyTestApiController : ControllerBase
{
    // GET /api/MyTestApi
    [HttpGet]
    public IActionResult Get()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Предыдущий метод Get возвращает Order = 2, Template = api/MyTestApi.The preceding Get method returns Order = 2, Template = api/MyTestApi.

Использование модели приложения для настройки маршрутов к атрибутамUse application model to customize attribute routes

Модель приложения:The application model:

  • Объектная модель, создаваемая при запуске.Is an object model created at startup.
  • Содержит все метаданные, используемые ASP.NET Core для маршрутизации и выполнения действий в приложении.Contains all of the metadata used by ASP.NET Core to route and execute the actions in an app.

Модель приложения включает все данные, собранные из атрибутов маршрута.The application model includes all of the data gathered from route attributes. Данные из атрибутов маршрута предоставляются реализацией IRouteTemplateProvider.The data from route attributes is provided by the IRouteTemplateProvider implementation. ИменованConventions:

  • Можно написать, чтобы изменить модель приложения, чтобы настроить маршрутизацию.Can be written to modify the application model to customize how routing behaves.
  • Считываются при запуске приложения.Are read at app startup.

В этом разделе показан простой пример настройки маршрутизации с помощью модели приложения.This section shows a basic example of customizing routing using application model. Следующий код делает маршруты примерно в соответствии со структурой папок проекта.The following code makes routes roughly line up with the folder structure of the project.

public class NamespaceRoutingConvention : Attribute, IControllerModelConvention
{
    private readonly string _baseNamespace;

    public NamespaceRoutingConvention(string baseNamespace)
    {
        _baseNamespace = baseNamespace;
    }

    public void Apply(ControllerModel controller)
    {
        var hasRouteAttributes = controller.Selectors.Any(selector =>
                                                selector.AttributeRouteModel != null);
        if (hasRouteAttributes)
        {
            return;
        }

        var namespc = controller.ControllerType.Namespace;
        if (namespc == null)
            return;
        var template = new StringBuilder();
        template.Append(namespc, _baseNamespace.Length + 1,
                        namespc.Length - _baseNamespace.Length - 1);
        template.Replace('.', '/');
        template.Append("/[controller]/[action]/{id?}");

        foreach (var selector in controller.Selectors)
        {
            selector.AttributeRouteModel = new AttributeRouteModel()
            {
                Template = template.ToString()
            };
        }
    }
}

Следующий код предотвращает применение namespaceного соглашения к контроллерам, которые направляются по атрибуту:The following code prevents the namespace convention from being applied to controllers that are attribute routed:

public void Apply(ControllerModel controller)
{
    var hasRouteAttributes = controller.Selectors.Any(selector =>
                                            selector.AttributeRouteModel != null);
    if (hasRouteAttributes)
    {
        return;
    }

Например, следующий контроллер не использует NamespaceRoutingConvention:For example, the following controller doesn't use NamespaceRoutingConvention:

[Route("[controller]/[action]/{id?}")]
public class ManagersController : Controller
{
    // /managers/index
    public IActionResult Index()
    {
        var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
        return Content($"Index- template:{template}");
    }

    public IActionResult List(int? id)
    {
        var path = Request.Path.Value;
        return Content($"List- Path:{path}");
    }
}

Метод NamespaceRoutingConvention.Apply:The NamespaceRoutingConvention.Apply method:

  • Не выполняет никаких действий, если контроллер является перенаправляемым атрибутом.Does nothing if the controller is attribute routed.
  • Задает шаблон контроллеров на основе namespaceс удалением базового namespace.Sets the controllers template based on the namespace, with the base namespace removed.

NamespaceRoutingConvention можно применить в Startup.ConfigureServices:The NamespaceRoutingConvention can be applied in Startup.ConfigureServices:

namespace My.Application
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews(options =>
            {
                options.Conventions.Add(
                    new NamespaceRoutingConvention(typeof(Startup).Namespace));
            });
        }
        // Remaining code ommitted for brevity.

Например, рассмотрим следующий контроллер:For example, consider the following controller:

using Microsoft.AspNetCore.Mvc;

namespace My.Application.Admin.Controllers
{
    public class UsersController : Controller
    {
        // GET /admin/controllers/users/index
        public IActionResult Index()
        {
            var fullname = typeof(UsersController).FullName;
            var template = 
                ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
            var path = Request.Path.Value;

            return Content($"Path: {path} fullname: {fullname}  template:{template}");
        }

        public IActionResult List(int? id)
        {
            var path = Request.Path.Value;
            return Content($"Path: {path} ID:{id}");
        }
    }
}

В приведенном выше коде:In the preceding code:

  • Базовый namespace My.Application.The base namespace is My.Application.
  • Полное имя предыдущего контроллера — My.Application.Admin.Controllers.UsersController.The full name of the preceding controller is My.Application.Admin.Controllers.UsersController.
  • NamespaceRoutingConvention задает для шаблона контроллеров значение Admin/Controllers/Users/[action]/{id?.The NamespaceRoutingConvention sets the controllers template to Admin/Controllers/Users/[action]/{id?.

NamespaceRoutingConvention также можно применить в качестве атрибута в контроллере:The NamespaceRoutingConvention can also be applied as an attribute on a controller:

[NamespaceRoutingConvention("My.Application")]
public class TestController : Controller
{
    // /admin/controllers/test/index
    public IActionResult Index()
    {
        var template = ControllerContext.ActionDescriptor.AttributeRouteInfo?.Template;
        var actionname = ControllerContext.ActionDescriptor.ActionName;
        return Content($"Action- {actionname} template:{template}");
    }

    public IActionResult List(int? id)
    {
        var path = Request.Path.Value;
        return Content($"List- Path:{path}");
    }
}

Смешанная маршрутизация с помощью атрибутов и на основе соглашенийMixed routing: Attribute routing vs conventional routing

ASP.NET Core приложения могут сочетать использование обычной маршрутизации и маршрутизации атрибутов.ASP.NET Core apps can mix the use of conventional routing and attribute routing. Маршруты на основе соглашений часто применяются для контроллеров, предоставляющих HTML-страницы для браузеров, а маршруты на основе атрибутов — для контроллеров, предоставляющих интерфейсы REST API.It's typical to use conventional routes for controllers serving HTML pages for browsers, and attribute routing for controllers serving REST APIs.

Действия маршрутизируются либо на основе соглашений, либо с помощью атрибутов.Actions are either conventionally routed or attribute routed. При добавлении маршрута к контроллеру или действию они становятся маршрутизируемыми с помощью атрибутов.Placing a route on the controller or the action makes it attribute routed. Действия, определяющие маршруты на основе атрибутов, недоступны по маршрутам на основе соглашений, и наоборот.Actions that define attribute routes cannot be reached through the conventional routes and vice-versa. Любой атрибут маршрута на контроллере делает все действия в атрибуте Controller перенаправлены.Any route attribute on the controller makes all actions in the controller attribute routed.

Маршрутизация атрибутов и Обычная маршрутизация используют один и тот же механизм маршрутизации.Attribute routing and conventional routing use the same routing engine.

Создание URL-адресов и значения окруженияURL Generation and ambient values

Приложения могут использовать функции создания URL-адресов маршрутизации для создания URL-ссылок на действия.Apps can use routing URL generation features to generate URL links to actions. Создание URL-адресов устраняет прописано URL-адреса, делая код более надежным и сопровождаемым.Generating URLs eliminates hardcoding URLs, making code more robust and maintainable. В этом разделе рассматриваются функции создания URL-адресов, предоставляемые MVC, и описываются только основные принципы работы формирования URL-адресов.This section focuses on the URL generation features provided by MVC and only cover basics of how URL generation works. Подробное описание формирования URL-адреса см. в статье Маршрутизация.See Routing for a detailed description of URL generation.

Интерфейс IUrlHelper является базовым элементом инфраструктуры между MVC и маршрутизацией для создания URL-адресов.The IUrlHelper interface is the underlying element of infrastructure between MVC and routing for URL generation. Экземпляр IUrlHelper доступен через свойство Url в контроллерах, представлениях и компонентах представления.An instance of IUrlHelper is available through the Url property in controllers, views, and view components.

В следующем примере интерфейс IUrlHelper используется с помощью свойства Controller.Url для создания URL-адреса другого действия.In the following example, the IUrlHelper interface is used through the Controller.Url property to generate a URL to another action.

public class UrlGenerationController : Controller
{
    public IActionResult Source()
    {
        // Generates /UrlGeneration/Destination
        var url = Url.Action("Destination");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    public IActionResult Destination()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

Если приложение использует стандартный маршрут по умолчанию, значением переменной url является строка URL-пути /UrlGeneration/Destination.If the app is using the default conventional route, the value of the url variable is the URL path string /UrlGeneration/Destination. Этот путь URL-адреса создается при маршрутизации путем объединения:This URL path is created by routing by combining:

  • Значения маршрута из текущего запроса, которые называются внешними значениями.The route values from the current request, which are called ambient values.
  • Значения, передаваемые в Url.Action и подставив эти значения в шаблон маршрута:The values passed to Url.Action and substituting those values into the route template:
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}

result: /UrlGeneration/Destination

Значение каждого параметра маршрута в шаблоне маршрута заменяется соответствующими именами со значениями и значениями окружения.Each route parameter in the route template has its value substituted by matching names with the values and ambient values. Параметр маршрута, который не имеет значения, может:A route parameter that doesn't have a value can:

  • Используйте значение по умолчанию, если таковое имеется.Use a default value if it has one.
  • Пропускается, если он необязателен.Be skipped if it's optional. Например, id из шаблона маршрута {controller}/{action}/{id?}.For example, the id from the route template {controller}/{action}/{id?}.

Создание URL-адреса завершается ошибкой, если ни один из обязательных параметров маршрута не имеет соответствующего значения.URL generation fails if any required route parameter doesn't have a corresponding value. Если для маршрута не удалось сформировать URL-адрес, проверяется следующий маршрут, пока не будут проверены все маршруты или не будет найдено соответствие.If URL generation fails for a route, the next route is tried until all routes have been tried or a match is found.

В предыдущем примере Url.Action предполагается Обычная маршрутизация.The preceding example of Url.Action assumes conventional routing. Создание URL-адреса работает аналогично маршрутизации атрибутов, хотя понятия различны.URL generation works similarly with attribute routing, though the concepts are different. С обычной маршрутизацией:With conventional routing:

  • Значения маршрута используются для расширения шаблона.The route values are used to expand a template.
  • Значения маршрута для controller и action обычно отображаются в этом шаблоне.The route values for controller and action usually appear in that template. Это работает потому, что URL-адреса, соответствующие маршрутизации, соответствуют соглашению.This works because the URLs matched by routing adhere to a convention.

В следующем примере используется маршрутизация атрибутов:The following example uses attribute routing:

public class UrlGenerationAttrController : Controller
{
    [HttpGet("custom")]
    public IActionResult Source()
    {
        var url = Url.Action("Destination");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    [HttpGet("custom/url/to/destination")]
    public IActionResult Destination()
    {
       return ControllerContext.MyDisplayRouteInfo();
    }
}

Source действие в предыдущем коде создает custom/url/to/destination.The Source action in the preceding code generates custom/url/to/destination.

LinkGenerator был добавлен в ASP.NET Core 3,0 в качестве альтернативы IUrlHelper.LinkGenerator was added in ASP.NET Core 3.0 as an alternative to IUrlHelper. LinkGenerator предлагает аналогичные, но более гибкие функции.LinkGenerator offers similar but more flexible functionality. Все остальные методы в IUrlHelper также имеют соответствующее семейство методов для LinkGenerator.Each other the methods on IUrlHelper has a corresponding family of methods on LinkGenerator as well.

Формирование URL-адресов по имени действияGenerating URLs by action name

URL. Action, линкженератор. жетпасбяктиони все связанные перегрузки предназначены для создания целевой конечной точки путем указания имени контроллера и имени действия.Url.Action, LinkGenerator.GetPathByAction, and all related overloads all are designed to generate the target endpoint by specifying a controller name and action name.

При использовании Url.Actionтекущие значения маршрута для controller и action предоставляются средой выполнения:When using Url.Action, the current route values for controller and action are provided by the runtime:

  • Значение controller и action являются частью как значений окружающих элементов, так и значений.The value of controller and action are part of both ambient values and values. Метод Url.Action всегда использует текущие значения action и controller и создает URL-путь, который направляет в текущее действие.The method Url.Action always uses the current values of action and controller and generates a URL path that routes to the current action.

Маршрутизация пытается использовать значения во внешних значениях для заполнения сведений, которые не были предоставлены при формировании URL-адреса.Routing attempts to use the values in ambient values to fill in information that wasn't provided when generating a URL. Рассмотрим такой маршрут, как {a}/{b}/{c}/{d} со значениями окружения { a = Alice, b = Bob, c = Carol, d = David }:Consider a route like {a}/{b}/{c}/{d} with ambient values { a = Alice, b = Bob, c = Carol, d = David }:

  • Маршрутизация содержит достаточно информации для создания URL-адреса без дополнительных значений.Routing has enough information to generate a URL without any additional values.
  • Маршрутизация содержит достаточно информации, так как все параметры маршрута имеют значение.Routing has enough information because all route parameters have a value.

Если добавляется значение { d = Donovan }:If the value { d = Donovan } is added:

  • Значение { d = David } игнорируется.The value { d = David } is ignored.
  • Путь к созданному URL-адресу — Alice/Bob/Carol/Donovan.The generated URL path is Alice/Bob/Carol/Donovan.

Предупреждение: URL-пути являются иерархическими.Warning: URL paths are hierarchical. В предыдущем примере, если добавляется значение { c = Cheryl }:In the preceding example, if the value { c = Cheryl } is added:

  • Оба значения { c = Carol, d = David } игнорируются.Both of the values { c = Carol, d = David } are ignored.
  • Больше нет значения для d и создание URL-адреса завершается неудачей.There is no longer a value for d and URL generation fails.
  • Для создания URL-адреса необходимо указать требуемые значения c и d.The desired values of c and d must be specified to generate a URL.

Возможно, вы намерены столкнуться с этой проблемой с {controller}/{action}/{id?}маршрута по умолчанию.You might expect to hit this problem with the default route {controller}/{action}/{id?}. Эта проблема возникает редко, поскольку Url.Action всегда явно указывает controller и action значение.This problem is rare in practice because Url.Action always explicitly specifies a controller and action value.

Несколько перегрузок URL. Action принимают объект значений маршрута, чтобы предоставить значения для параметров маршрута, отличных от controller и action.Several overloads of Url.Action take a route values object to provide values for route parameters other than controller and action. Объект значений маршрута часто используется с id.The route values object is frequently used with id. Например, Url.Action("Buy", "Products", new { id = 17 }).For example, Url.Action("Buy", "Products", new { id = 17 }). Объект значений маршрута:The route values object:

  • По соглашению обычно является объектом анонимного типа.By convention is usually an object of anonymous type.
  • Может быть IDictionary<> или POCO).Can be an IDictionary<> or a POCO).

Остальные значения маршрута, которые не соответствуют параметрам маршрута, помещаются в строку запроса.Any additional route values that don't match route parameters are put in the query string.

public IActionResult Index()
{
    var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
    return Content(url);
}

Приведенный выше код создает /Products/Buy/17?color=red.The preceding code generates /Products/Buy/17?color=red.

Следующий код создает абсолютный URL-адрес:The following code generates an absolute URL:

public IActionResult Index2()
{
    var url = Url.Action("Buy", "Products", new { id = 17 }, protocol: Request.Scheme);
    // Returns https://localhost:5001/Products/Buy/17
    return Content(url);
}

Чтобы создать абсолютный URL-адрес, используйте один из следующих способов.To create an absolute URL, use one of the following:

Создание URL-адресов по маршрутуGenerate URLs by route

Приведенный выше код демонстрирует создание URL-адреса путем передачи контроллера и имени действия.The preceding code demonstrated generating a URL by passing in the controller and action name. IUrlHelper также предоставляет семейство методов URL. RouteUrl .IUrlHelper also provides the Url.RouteUrl family of methods. Эти методы похожи на URL. Action, но не копируют текущие значения action и controller в значения маршрута.These methods are similar to Url.Action, but they don't copy the current values of action and controller to the route values. Наиболее распространенное использование Url.RouteUrl:The most common usage of Url.RouteUrl:

  • Указывает имя маршрута для создания URL-адреса.Specifies a route name to generate the URL.
  • Обычно не указывает имя контроллера или действия.Generally doesn't specify a controller or action name.
public class UrlGeneration2Controller : Controller
{
    [HttpGet("")]
    public IActionResult Source()
    {
        var url = Url.RouteUrl("Destination_Route");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    [HttpGet("custom/url/to/destination2", Name = "Destination_Route")]
    public IActionResult Destination()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

Следующий файл Razor создает ссылку HTML на Destination_Route:The following Razor file generates an HTML link to the Destination_Route:

<h1>Test Links</h1>

<ul>
    <li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>

Создание URL-адресов в HTML и RazorGenerate URLs in HTML and Razor

IHtmlHelper предоставляет HtmlHelper методов HTML. бегинформ и HTML. ActionLink для создания элементов <form> и <a> соответственно.IHtmlHelper provides the HtmlHelper methods Html.BeginForm and Html.ActionLink to generate <form> and <a> elements respectively. Эти методы используют метод URL. Action для создания URL-адреса и принимают аналогичные аргументы.These methods use the Url.Action method to generate a URL and they accept similar arguments. Эквивалентами методов Url.RouteUrl для HtmlHelper являются методы Html.BeginRouteForm и Html.RouteLink, которые имеют схожие функции.The Url.RouteUrl companions for HtmlHelper are Html.BeginRouteForm and Html.RouteLink which have similar functionality.

Для формирования URL-адресов используются вспомогательные функции тегов form и <a>.TagHelpers generate URLs through the form TagHelper and the <a> TagHelper. Обе они реализуются с помощью интерфейса IUrlHelper.Both of these use IUrlHelper for their implementation. Дополнительные сведения см. в разделе вспомогательные функции тегов в формах .See Tag Helpers in forms for more information.

Внутри представлений интерфейс IUrlHelper доступен посредством свойства Url для особых случаев формирования URL-адресов, помимо описанных выше.Inside views, the IUrlHelper is available through the Url property for any ad-hoc URL generation not covered by the above.

Создание URL-адресов в результатах действийURL generation in Action Results

В предыдущих примерах показано использование IUrlHelper в контроллере.The preceding examples showed using IUrlHelper in a controller. Наиболее распространенным использованием контроллера является создание URL-адреса как части результата действия.The most common usage in a controller is to generate a URL as part of an action result.

Базовые классы ControllerBase и Controller предоставляют удобные методы для результатов действий, ссылающихся на другое действие.The ControllerBase and Controller base classes provide convenience methods for action results that reference another action. Один из типичных способов использования — перенаправление после приема входных данных пользователя:One typical usage is to redirect after accepting user input:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, Customer customer)
{
    if (ModelState.IsValid)
    {
        // Update DB with new details.
        ViewData["Message"] = $"Successful edit of customer {id}";
        return RedirectToAction("Index");
    }
    return View(customer);
}

Такие методы фабрики результатов действий, как RedirectToAction и CreatedAtAction, соответствуют методам в IUrlHelper.The action results factory methods such as RedirectToAction and CreatedAtAction follow a similar pattern to the methods on IUrlHelper.

Выделенные маршруты на основе соглашенийSpecial case for dedicated conventional routes

В обычной маршрутизации может использоваться особый тип определения маршрута, называемый выделенным обычным маршрутом.Conventional routing can use a special kind of route definition called a dedicated conventional route. В следующем примере маршрут с именем blog является выделенным обычным маршрутом:In the following example, the route named blog is a dedicated conventional route:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(name: "blog",
                pattern: "blog/{*article}",
                defaults: new { controller = "Blog", action = "Article" });
    endpoints.MapControllerRoute(name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
});

Используя предыдущие определения маршрутов, Url.Action("Index", "Home") создает URL-путь / используя default маршрут, но почему?Using the preceding route definitions, Url.Action("Index", "Home") generates the URL path / using the default route, but why? Можно было бы предположить, что значений маршрута { controller = Home, action = Index } было бы достаточно для формирования URL-адреса с помощью blog и результатом было бы /blog?action=Index&controller=Home.You might guess the route values { controller = Home, action = Index } would be enough to generate a URL using blog, and the result would be /blog?action=Index&controller=Home.

Выделенные традиционные маршруты полагаются на особое поведение значений по умолчанию, которые не имеют соответствующего параметра маршрута, который предотвращает слишком жадную маршрутизацию при формировании URL-адресов.Dedicated conventional routes rely on a special behavior of default values that don't have a corresponding route parameter that prevents the route from being too greedy with URL generation. В этом случае значения по умолчанию — { controller = Blog, action = Article }, но параметров маршрута controller и action нет.In this case the default values are { controller = Blog, action = Article }, and neither controller nor action appears as a route parameter. Когда система маршрутизации производит формирование URL-адреса, предоставленные значения должны соответствовать значениям по умолчанию.When routing performs URL generation, the values provided must match the default values. Создание URL-адреса с помощью blog завершается сбоем, так как значения { controller = Home, action = Index } не совпадают { controller = Blog, action = Article }.URL generation using blog fails because the values { controller = Home, action = Index } don't match { controller = Blog, action = Article }. После этого система маршрутизации выполнит попытку использовать маршрут default, которая завершится успешно.Routing then falls back to try default, which succeeds.

ЗоныAreas

Области — это функция MVC, используемая для упорядочивания связанных функций в группе в виде отдельных:Areas are an MVC feature used to organize related functionality into a group as a separate:

  • Пространство имен маршрутизации для действий контроллера.Routing namespace for controller actions.
  • Структура папок для представлений.Folder structure for views.

Использование областей позволяет приложению иметь несколько контроллеров с одинаковым именем, если они имеют разные области.Using areas allows an app to have multiple controllers with the same name, as long as they have different areas. При использовании областей создается иерархия в целях маршрутизации. Для этого к area и controller добавляется еще один параметр маршрута, action.Using areas creates a hierarchy for the purpose of routing by adding another route parameter, area to controller and action. В этом разделе обсуждается взаимодействие маршрутизации с областями.This section discusses how routing interacts with areas. Сведения об использовании областей с представлениями см. в разделе области .See Areas for details about how areas are used with views.

В следующем примере MVC настраивается для использования стандартного маршрута по умолчанию и маршрута area для area с именем Blog.The following example configures MVC to use the default conventional route and an area route for an area named Blog:

app.UseEndpoints(endpoints =>
{
    endpoints.MapAreaControllerRoute("blog_route", "Blog",
        "Manage/{controller}/{action}/{id?}");
    endpoints.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
});

В приведенном выше коде вызывается MapAreaControllerRoute для создания "blog_route".In the preceding code, MapAreaControllerRoute is called to create the "blog_route". Вторым параметром, "Blog", является имя области.The second parameter, "Blog", is the area name.

При сопоставлении URL-пути, например /Manage/Users/AddUser, "blog_route" маршрут создает значения маршрута { area = Blog, controller = Users, action = AddUser }.When matching a URL path like /Manage/Users/AddUser, the "blog_route" route generates the route values { area = Blog, controller = Users, action = AddUser }. area значение маршрута создается значением по умолчанию для area.The area route value is produced by a default value for area. Маршрут, созданный MapAreaControllerRoute, эквивалентен следующему:The route created by MapAreaControllerRoute is equivalent to the following:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute("blog_route", "Manage/{controller}/{action}/{id?}",
        defaults: new { area = "Blog" }, constraints: new { area = "Blog" });
    endpoints.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
});

Метод MapAreaControllerRoute создает маршрут с помощью значения по умолчанию и ограничения для area с использованием предоставленного имени маршрута (в данном случае Blog).MapAreaControllerRoute creates a route using both a default value and constraint for area using the provided area name, in this case Blog. Значение по умолчанию гарантирует, что маршрут всегда создает значение { area = Blog, ... }. Ограничение требует значения { area = Blog, ... } для формирования URL-адреса.The default value ensures that the route always produces { area = Blog, ... }, the constraint requires the value { area = Blog, ... } for URL generation.

При маршрутизации на основе соглашений учитывается порядок.Conventional routing is order-dependent. Как правило, маршруты с областями следует размещать раньше, так как они более специфичны, чем маршруты без области.In general, routes with areas should be placed earlier as they're more specific than routes without an area.

Используя приведенный выше пример, значения маршрута { area = Blog, controller = Users, action = AddUser } соответствовать следующему действию:Using the preceding example, the route values { area = Blog, controller = Users, action = AddUser } match the following action:

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace1
{
    [Area("Blog")]
    public class UsersController : Controller
    {
        // GET /manage/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}

Атрибут [Area] указывает, что контроллер является частью области.The [Area] attribute is what denotes a controller as part of an area. Этот контроллер находится в области Blog.This controller is in the Blog area. Контроллеры без атрибута [Area] не являются членами какой-либо области и не соответствуют, если значение маршрута area предоставляется службой маршрутизации.Controllers without an [Area] attribute are not members of any area, and do not match when the area route value is provided by routing. В приведенном ниже примере только первый контроллер может соответствовать значениям маршрута { area = Blog, controller = Users, action = AddUser }.In the following example, only the first controller listed can match the route values { area = Blog, controller = Users, action = AddUser }.

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace1
{
    [Area("Blog")]
    public class UsersController : Controller
    {
        // GET /manage/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace2
{
    // Matches { area = Zebra, controller = Users, action = AddUser }
    [Area("Zebra")]
    public class UsersController : Controller
    {
        // GET /zebra/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace3
{
    // Matches { area = string.Empty, controller = Users, action = AddUser }
    // Matches { area = null, controller = Users, action = AddUser }
    // Matches { controller = Users, action = AddUser }
    public class UsersController : Controller
    {
        // GET /users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }
    }
}

Пространство имен каждого контроллера показано здесь для полноты.The namespace of each controller is shown here for completeness. Если предыдущие контроллеры используют одно и то же пространство имен, создается ошибка компилятора.If the preceding controllers uses the same namespace, a compiler error would be generated. Имена пространств классов не влияют на маршрутизацию в MVC.Class namespaces have no effect on MVC's routing.

Первые два контроллера входят в области и будут соответствовать запросу, только если соответствующее имя области предоставлено значением маршрута area.The first two controllers are members of areas, and only match when their respective area name is provided by the area route value. Третий контроллер не входит ни в одну область и может соответствовать запросу, только если значение area не предоставлено системой маршрутизации.The third controller isn't a member of any area, and can only match when no value for area is provided by routing.

В плане сопоставления отсутствующих значений отсутствие значения area равносильно тому, как если значением area было бы NULL или пустая строка.In terms of matching no value, the absence of the area value is the same as if the value for area were null or the empty string.

При выполнении действия в области значение маршрута для area доступно в качестве внешнего значения для маршрутизации, используемой для формирования URL-адреса.When executing an action inside an area, the route value for area is available as an ambient value for routing to use for URL generation. Это означает, что по умолчанию области являются фиксированными при формировании URL-адресов, как показано в следующем примере.This means that by default areas act sticky for URL generation as demonstrated by the following sample.

app.UseEndpoints(endpoints =>
{
    endpoints.MapAreaControllerRoute(name: "duck_route", 
                                     areaName: "Duck",
                                     pattern: "Manage/{controller}/{action}/{id?}");
    endpoints.MapControllerRoute(name: "default",
                                 pattern: "Manage/{controller=Home}/{action=Index}/{id?}");
});
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace4
{
    [Area("Duck")]
    public class UsersController : Controller
    {
        // GET /Manage/users/GenerateURLInArea
        public IActionResult GenerateURLInArea()
        {
            // Uses the 'ambient' value of area.
            var url = Url.Action("Index", "Home");
            // Returns /Manage/Home/Index
            return Content(url);
        }

        // GET /Manage/users/GenerateURLOutsideOfArea
        public IActionResult GenerateURLOutsideOfArea()
        {
            // Uses the empty value for area.
            var url = Url.Action("Index", "Home", new { area = "" });
            // Returns /Manage
            return Content(url);
        }
    }
}

Следующий код создает URL-адрес для /Zebra/Users/AddUser:The following code generates a URL to /Zebra/Users/AddUser:

public class HomeController : Controller
{
    public IActionResult About()
    {
        var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
        return Content($"URL: {url}");
    }

Определение действияAction definition

Открытые методы на контроллере, за исключением тех, которые имеют атрибут недействия , являются действиями.Public methods on a controller, except those with the NonAction attribute, are actions.

Образец кодаSample code

Отладка диагностикиDebug diagnostics

Для подробного вывода диагностики построения маршрутов задайте для Logging:LogLevel:Microsoft значение Debug.For detailed routing diagnostic output, set Logging:LogLevel:Microsoft to Debug. Например, в среде разработки задайте appsettings.Development.json:For example, in the development environment, set appsettings.Development.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Debug",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}

В ASP.NET Core MVC используется ПО промежуточного слоя маршрутизации для сопоставления URL-адресов входящих запросов с действиями.ASP.NET Core MVC uses the Routing middleware to match the URLs of incoming requests and map them to actions. Маршруты определяются в коде запуска или атрибутах.Routes are defined in startup code or attributes. Они описывают то, как пути URL-адресов должны сопоставляться с действиями.Routes describe how URL paths should be matched to actions. С помощью маршрутов также формируются URL-адреса (для ссылок), отправляемые в ответах.Routes are also used to generate URLs (for links) sent out in responses.

Действия маршрутизируются либо на основе соглашений, либо с помощью атрибутов.Actions are either conventionally routed or attribute routed. При добавлении маршрута к контроллеру или действию они становятся маршрутизируемыми с помощью атрибутов.Placing a route on the controller or the action makes it attribute routed. Дополнительные сведения см. в разделе Смешанная маршрутизация.See Mixed routing for more information.

В этом документе описывается взаимодействие между MVC и маршрутизацией и использование возможностей маршрутизации в типичных приложениях MVC.This document will explain the interactions between MVC and routing, and how typical MVC apps make use of routing features. Подробные сведения о расширенной маршрутизации см. в статье Маршрутизация.See Routing for details on advanced routing.

Настройка ПО промежуточного слоя маршрутизацииSetting up Routing Middleware

Метод Configure содержит код, который выглядит примерно так:In your Configure method you may see code similar to:

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

В вызове UseMvc метод MapRoute используется для создания одного маршрута, который называется маршрутом default.Inside the call to UseMvc, MapRoute is used to create a single route, which we'll refer to as the default route. В большинстве приложений MVC используется маршрут с шаблоном, сходным с маршрутом default.Most MVC apps will use a route with a template similar to the default route.

Шаблон маршрута "{controller=Home}/{action=Index}/{id?}" может сопоставлять путь URL-адреса, например /Products/Details/5, и извлекает значения маршрута { controller = Products, action = Details, id = 5 } путем разбивки пути на лексемы.The route template "{controller=Home}/{action=Index}/{id?}" can match a URL path like /Products/Details/5 and will extract the route values { controller = Products, action = Details, id = 5 } by tokenizing the path. MVC попытается найти контроллер с именем ProductsController и выполнить действие Details:MVC will attempt to locate a controller named ProductsController and run the action Details:

public class ProductsController : Controller
{
   public IActionResult Details(int id) { ... }
}

Обратите внимание на то, что в этом примере привязка модели будет использовать значение id = 5, чтобы присвоить параметру id значение 5 при вызове действия.Note that in this example, model binding would use the value of id = 5 to set the id parameter to 5 when invoking this action. Дополнительные сведения см. в разделе Привязка модели.See the Model Binding for more details.

Использование маршрута default:Using the default route:

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

Шаблон маршрута:The route template:

  • {controller=Home} определяет Home в качестве объекта controller по умолчанию.{controller=Home} defines Home as the default controller

  • {action=Index} определяет Index в качестве объекта action по умолчанию.{action=Index} defines Index as the default action

  • {id?} определяет необязательный параметр id.{id?} defines id as optional

Параметры маршрута по умолчанию и необязательные параметры необязательно должны присутствовать в пути URL-адреса для сопоставления.Default and optional route parameters don't need to be present in the URL path for a match. Подробное описание синтаксиса шаблона маршрута см. в разделе Справочник по шаблону маршрута.See Route Template Reference for a detailed description of route template syntax.

"{controller=Home}/{action=Index}/{id?}" может сопоставляться с путем URL-адреса / и выдает значения маршрута { controller = Home, action = Index }."{controller=Home}/{action=Index}/{id?}" can match the URL path / and will produce the route values { controller = Home, action = Index }. Для объектов controller и action используются значения по умолчанию. id не имеет значения, так как в пути URL-адреса нет соответствующего сегмента.The values for controller and action make use of the default values, id doesn't produce a value since there's no corresponding segment in the URL path. MVC будет использовать эти значения маршрута для выбора действия HomeController и Index:MVC would use these route values to select the HomeController and Index action:

public class HomeController : Controller
{
  public IActionResult Index() { ... }
}

При использовании этого определения контроллера и шаблона маршрута действие HomeController.Index будет выполняться для любых из следующих путей URL-адресов:Using this controller definition and route template, the HomeController.Index action would be executed for any of the following URL paths:

  • /Home/Index/17

  • /Home/Index

  • /Home

  • /

Универсальный метод UseMvcWithDefaultRoute:The convenience method UseMvcWithDefaultRoute:

app.UseMvcWithDefaultRoute();

Можно использовать вместо следующего метода:Can be used to replace:

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

UseMvc и UseMvcWithDefaultRoute добавляют экземпляр RouterMiddleware в конвейер ПО промежуточного слоя.UseMvc and UseMvcWithDefaultRoute add an instance of RouterMiddleware to the middleware pipeline. MVC не взаимодействует с ПО промежуточного слоя напрямую, а использует маршрутизацию для обработки запросов.MVC doesn't interact directly with middleware, and uses routing to handle requests. MVC подключается к маршрутам посредством экземпляра MvcRouteHandler.MVC is connected to the routes through an instance of MvcRouteHandler. Код метода UseMvc имеет примерно следующий вид:The code inside of UseMvc is similar to the following:

var routes = new RouteBuilder(app);

// Add connection to MVC, will be hooked up by calls to MapRoute.
routes.DefaultHandler = new MvcRouteHandler(...);

// Execute callback to register routes.
// routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");

// Create route collection and add the middleware.
app.UseRouter(routes.Build());

Метод UseMvc не определяет маршруты напрямую, а добавляет заполнитель в коллекцию маршрутов для маршрута attribute.UseMvc doesn't directly define any routes, it adds a placeholder to the route collection for the attribute route. Перегрузка UseMvc(Action<IRouteBuilder>) позволяет добавлять собственные маршруты, а также поддерживает маршрутизацию с помощью атрибутов.The overload UseMvc(Action<IRouteBuilder>) lets you add your own routes and also supports attribute routing. Метод UseMvc и все его варианты добавляют заполнитель для маршрута на основе атрибутов. Маршрутизация с помощью атрибутов доступна всегда вне зависимости от того, как настроен метод UseMvc.UseMvc and all of its variations add a placeholder for the attribute route - attribute routing is always available regardless of how you configure UseMvc. Метод UseMvcWithDefaultRoute определяет маршрут по умолчанию и поддерживает маршрутизацию с помощью атрибутов.UseMvcWithDefaultRoute defines a default route and supports attribute routing. В разделе Маршрутизация с помощью атрибутов приводятся более подробные сведения о маршрутизации с помощью атрибутов.The Attribute Routing section includes more details on attribute routing.

Маршрутизация на основе соглашенийConventional routing

Маршрут default:The default route:

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

Приведенный выше код является примером обычной маршрутизации.The preceding code is an example of a conventional routing. Этот стиль называется обычной маршрутизацией, так как он устанавливает соглашение для URL-путей:This style is called conventional routing because it establishes a convention for URL paths:

  • Первый сегмент пути сопоставляется с именем контроллера.The first path segment maps to the controller name.
  • Второй сопоставляется с именем действия.The second maps to the action name.
  • Третий сегмент используется для необязательных id.The third segment is used for an optional id. id сопоставляется с сущностью модели.id maps to a model entity.

При использовании этого маршрута default путь URL-адреса /Products/List сопоставляется с действием ProductsController.List, а путь /Blog/Article/17 сопоставляется с BlogController.Article.Using this default route, the URL path /Products/List maps to the ProductsController.List action, and /Blog/Article/17 maps to BlogController.Article. Такое сопоставление основывается только на именах контроллера и действия, но не на пространствах имен, расположениях исходных файлов или параметрах метода.This mapping is based on the controller and action names only and isn't based on namespaces, source file locations, or method parameters.

Совет

Использование маршрутизации на основе соглашений с маршрутом по умолчанию позволяет быстро создавать приложение, не придумывая новый шаблон URL-адреса для каждого определяемого действия.Using conventional routing with the default route allows you to build the application quickly without having to come up with a new URL pattern for each action you define. В случае с приложением с действиями в стиле CRUD единообразие URL-адресов для всех контроллеров позволяет упростить код и сделать пользовательский интерфейс более предсказуемым.For an application with CRUD style actions, having consistency for the URLs across your controllers can help simplify your code and make your UI more predictable.

Предупреждение

Параметр id определяется в шаблоне маршрута как необязательный. Это означает, что действия могут выполняться, даже если идентификатор не указан в URL-адресе.The id is defined as optional by the route template, meaning that your actions can execute without the ID provided as part of the URL. Как правило, если параметр id отсутствует в URL-адресе, привязка модели присваивает ему значение 0, и в результате в базе данных не будет найдена сущность, соответствующая id == 0.Usually what will happen if id is omitted from the URL is that it will be set to 0 by model binding, and as a result no entity will be found in the database matching id == 0. Маршрутизация с помощью атрибутов обеспечивает детальный контроль, позволяя настраивать идентификатор как обязательный лишь для некоторых действий.Attribute routing can give you fine-grained control to make the ID required for some actions and not for others. В документации необязательные параметры, такие как id, будут включаться, только если они, скорее всего, могут использоваться в соответствующей ситуации.By convention the documentation will include optional parameters like id when they're likely to appear in correct usage.

Несколько маршрутовMultiple routes

В метод UseMvc можно добавить несколько маршрутов, добавив дополнительные вызовы MapRoute.You can add multiple routes inside UseMvc by adding more calls to MapRoute. Таким образом можно определить несколько соглашений или добавить маршруты на основе соглашений, предназначенные для определенного действия, например:Doing so allows you to define multiple conventions, or to add conventional routes that are dedicated to a specific action, such as:

app.UseMvc(routes =>
{
   routes.MapRoute("blog", "blog/{*article}",
            defaults: new { controller = "Blog", action = "Article" });
   routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
});

Маршрут blog здесь — это выделенный маршрут на основе соглашения. Это означает, что он использует систему маршрутизации на основе соглашений, но предназначен для определенного действия.The blog route here is a dedicated conventional route, meaning that it uses the conventional routing system, but is dedicated to a specific action. Так как параметры controller и action отсутствуют в шаблоне маршрута, они могут иметь только значения по умолчанию, поэтому этот маршрут всегда будет сопоставляться с действием BlogController.Article.Since controller and action don't appear in the route template as parameters, they can only have the default values, and thus this route will always map to the action BlogController.Article.

Маршруты в коллекции маршрутов упорядочены и обрабатываются в порядке добавления.Routes in the route collection are ordered, and will be processed in the order they're added. Поэтому в этом примере маршрут blog будет проверяться перед маршрутом default.So in this example, the blog route will be tried before the default route.

Примечание

Выделенные традиционные маршруты часто используют перекрестные параметры маршрута, такие как {*article} для записи оставшейся части пути URL-адреса.Dedicated conventional routes often use catch-all route parameters like {*article} to capture the remaining portion of the URL path. Из-за этого маршрут может оказаться слишком универсальным, то есть он будет соответствовать URL-адресам, которые должны сопоставляться с другими маршрутами.This can make a route 'too greedy' meaning that it matches URLs that you intended to be matched by other routes. Чтобы избежать этой проблемы, такие универсальные маршруты следует помещать в конце таблицы маршрутов.Put the 'greedy' routes later in the route table to solve this.

ОткатFallback

В ходе обработки запроса MVC проверяет, можно ли найти контроллер и действие в приложении с помощью предоставленных значений маршрута.As part of request processing, MVC will verify that the route values can be used to find a controller and action in your application. Если нет действия, соответствующего значениям, маршрут считается не сопоставленным, и проверяется следующий маршрут.If the route values don't match an action then the route isn't considered a match, and the next route will be tried. Этот процесс называется откатом и призван упростить обработку случаев, когда маршруты на основе соглашений перекрываются.This is called fallback, and it's intended to simplify cases where conventional routes overlap.

Разрешение неоднозначности действийDisambiguating actions

Если при маршрутизации найдены два соответствующих действия, платформа MVC должна устранить неоднозначность, выбрав наиболее подходящее из них, или создать исключение.When two actions match through routing, MVC must disambiguate to choose the 'best' candidate or else throw an exception. Например:For example:

public class ProductsController : Controller
{
   public IActionResult Edit(int id) { ... }

   [HttpPost]
   public IActionResult Edit(int id, Product product) { ... }
}

Этот контроллер определяет два действия, которые соответствуют пути URL-адреса /Products/Edit/17 и маршрутизируют данные { controller = Products, action = Edit, id = 17 }.This controller defines two actions that would match the URL path /Products/Edit/17 and route data { controller = Products, action = Edit, id = 17 }. Это типичный шаблон для контроллеров MVC, в котором метод Edit(int) отображает форму для изменения сведений о продукте, а метод Edit(int, Product) обрабатывает отправленную форму.This is a typical pattern for MVC controllers where Edit(int) shows a form to edit a product, and Edit(int, Product) processes the posted form. Чтобы это было возможным, платформа MVC должна выбрать Edit(int, Product) для HTTP-запроса POST и Edit(int) для любой другой HTTP-команды.To make this possible MVC would need to choose Edit(int, Product) when the request is an HTTP POST and Edit(int) when the HTTP verb is anything else.

HttpPostAttribute ([HttpPost]) — это реализация интерфейса IActionConstraint, которая позволяет выбрать действие, только если HTTP-команда — POST.The HttpPostAttribute ( [HttpPost] ) is an implementation of IActionConstraint that will only allow the action to be selected when the HTTP verb is POST. Наличие интерфейса IActionConstraint делает метод Edit(int, Product) более подходящим вариантом, чем Edit(int), поэтому Edit(int, Product) будет проверяться первым.The presence of an IActionConstraint makes the Edit(int, Product) a 'better' match than Edit(int), so Edit(int, Product) will be tried first.

Пользовательские реализации IActionConstraint потребуется создавать только в особых ситуациях, однако важно понимать роль таких атрибутов, как HttpPostAttribute, — аналогичные атрибуты определены для других HTTP-команд.You will only need to write custom IActionConstraint implementations in specialized scenarios, but it's important to understand the role of attributes like HttpPostAttribute - similar attributes are defined for other HTTP verbs. При маршрутизации на основе соглашений для действий часто используются одинаковые имена в рамках рабочего процесса show form -> submit form.In conventional routing it's common for actions to use the same action name when they're part of a show form -> submit form workflow. Удобство такого шаблона станет очевидным после ознакомления с разделом Сведения об интерфейсе IActionConstraint.The convenience of this pattern will become more apparent after reviewing the Understanding IActionConstraint section.

Если найдено несколько совпадений и MVC не может определить наиболее подходящий маршрут, создается исключение AmbiguousActionException.If multiple routes match, and MVC can't find a 'best' route, it will throw an AmbiguousActionException.

Имена маршрутовRoute names

Строки "blog" и "default" в следующих примерах представляют собой имена маршрутов:The strings "blog" and "default" in the following examples are route names:

app.UseMvc(routes =>
{
   routes.MapRoute("blog", "blog/{*article}",
               defaults: new { controller = "Blog", action = "Article" });
   routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
});

Имя маршрута — это логическое имя, которое позволяет использовать именованный маршрут для формирования URL-адреса.The route names give the route a logical name so that the named route can be used for URL generation. Имена значительно упрощают создание URL-адресов, если оно представляет сложность из-за порядка маршрутов.This greatly simplifies URL creation when the ordering of routes could make URL generation complicated. Имена маршрутов должны быть уникальными в пределах приложения.Route names must be unique application-wide.

Имена маршрутов не влияют на сопоставление URL-адресов или обработку запросов; они служат только для формирования URL-адресов.Route names have no impact on URL matching or handling of requests; they're used only for URL generation. В статье Маршрутизация приводятся более подробные сведения о формировании URL-адресов, в том числе во вспомогательных объектах MVC.Routing has more detailed information on URL generation including URL generation in MVC-specific helpers.

Маршрутизация с помощью атрибутовAttribute routing

При маршрутизации с помощью атрибутов используется набор атрибутов для сопоставления действий непосредственно с шаблонами маршрутов.Attribute routing uses a set of attributes to map actions directly to route templates. В приведенном ниже примере в методе app.UseMvc(); используется Configure, и маршрут не передается.In the following example, app.UseMvc(); is used in the Configure method and no route is passed. HomeController будет соответствовать набору URL-адресов, аналогичных тем, которым соответствует маршрут по умолчанию {controller=Home}/{action=Index}/{id?}:The HomeController will match a set of URLs similar to what the default route {controller=Home}/{action=Index}/{id?} would match:

public class HomeController : Controller
{
   [Route("")]
   [Route("Home")]
   [Route("Home/Index")]
   public IActionResult Index()
   {
      return View();
   }
   [Route("Home/About")]
   public IActionResult About()
   {
      return View();
   }
   [Route("Home/Contact")]
   public IActionResult Contact()
   {
      return View();
   }
}

Действие HomeController.Index() будет выполняться для любого из путей URL-адресов /, /Home или /Home/Index.The HomeController.Index() action will be executed for any of the URL paths /, /Home, or /Home/Index.

Примечание

В этом примере показано ключевое различие между маршрутизацией с помощью атрибутов и маршрутизацией на основе соглашений.This example highlights a key programming difference between attribute routing and conventional routing. При использовании маршрутизации с помощью атрибутов для указания маршрута требуется больше входных данных; маршрут по умолчанию на основе соглашения более лаконичен.Attribute routing requires more input to specify a route; the conventional default route handles routes more succinctly. Однако маршрутизация с помощью атрибутов обеспечивает более точный контроль над тем, какие шаблоны маршрутов применяются к каждому действию, и требует такого контроля.However, attribute routing allows (and requires) precise control of which route templates apply to each action.

В случае с маршрутизацией с помощью атрибутов имя контроллера и имена действий не имеют значения при выборе действия.With attribute routing the controller name and action names play no role in which action is selected. В этом примере сопоставление будет производиться с теми же URL-адресами, что и в предыдущем.This example will match the same URLs as the previous example.

public class MyDemoController : Controller
{
   [Route("")]
   [Route("Home")]
   [Route("Home/Index")]
   public IActionResult MyIndex()
   {
      return View("Index");
   }
   [Route("Home/About")]
   public IActionResult MyAbout()
   {
      return View("About");
   }
   [Route("Home/Contact")]
   public IActionResult MyContact()
   {
      return View("Contact");
   }
}

Примечание

Приведенные выше шаблоны маршрутов не определяют параметры маршрутов для action, area и controller.The route templates above don't define route parameters for action, area, and controller. По сути, эти параметры маршрутов не разрешены в маршрутах на основе атрибутов.In fact, these route parameters are not allowed in attribute routes. Так как шаблон маршрута уже связан с действием, не имеет смысла анализировать имя действия в URL-адресе.Since the route template is already associated with an action, it wouldn't make sense to parse the action name from the URL.

Маршрутизация с помощью атрибутов Http[Verb]Attribute routing with Http[Verb] attributes

При маршрутизации с помощью атрибутов также могут использоваться атрибуты Http[Verb], такие как HttpPostAttribute.Attribute routing can also make use of the Http[Verb] attributes such as HttpPostAttribute. Все эти атрибуты могут принимать шаблон маршрута.All of these attributes can accept a route template. В этом примере показаны два действия, которые совпадают с одним шаблоном маршрута:This example shows two actions that match the same route template:

[HttpGet("/products")]
public IActionResult ListProducts()
{
   // ...
}

[HttpPost("/products")]
public IActionResult CreateProduct(...)
{
   // ...
}

Для такого пути URL-адреса, как /products, действие ProductsApi.ListProducts будет выполнено, если HTTP-командой является GET, а действие ProductsApi.CreateProduct будет выполнено, если HTTP-командой является POST.For a URL path like /products the ProductsApi.ListProducts action will be executed when the HTTP verb is GET and ProductsApi.CreateProduct will be executed when the HTTP verb is POST. При маршрутизации с помощью атрибутов URL-адрес сначала сопоставляется с набором шаблоном маршрутов, определяемых атрибутами маршрутов.Attribute routing first matches the URL against the set of route templates defined by route attributes. После того как найден соответствующий шаблон маршрута, применяются ограничения IActionConstraint для определения действий, которые могут быть выполнены.Once a route template matches, IActionConstraint constraints are applied to determine which actions can be executed.

Совет

При разработке REST API редко приходится использовать [Route(...)] для метода действия, так как действие принимает все методы HTTP.When building a REST API, it's rare that you will want to use [Route(...)] on an action method as the action will accept all HTTP methods. Предпочтительнее применять более конкретные атрибуты Http*Verb*Attributes, чтобы точно определить поддерживаемые API возможности.It's better to use the more specific Http*Verb*Attributes to be precise about what your API supports. Клиенты интерфейсов REST API должны знать, какие пути и HTTP-команды сопоставляются с определенными логическими операциями.Clients of REST APIs are expected to know what paths and HTTP verbs map to specific logical operations.

Так как маршрут на основе атрибутов применяется к определенному действию, можно легко сделать параметры обязательными в рамках определения шаблона маршрута.Since an attribute route applies to a specific action, it's easy to make parameters required as part of the route template definition. В этом примере параметр id является обязательным в пути URL-адреса.In this example, id is required as part of the URL path.

public class ProductsApiController : Controller
{
   [HttpGet("/products/{id}", Name = "Products_List")]
   public IActionResult GetProduct(int id) { ... }
}

Действие ProductsApi.GetProduct(int) будет выполнено для такого пути URL-адреса, как /products/3, но не для такого пути URL-адреса, как /products.The ProductsApi.GetProduct(int) action will be executed for a URL path like /products/3 but not for a URL path like /products. Полное описание шаблонов маршрутов и связанных параметров см. в статье Маршрутизация.See Routing for a full description of route templates and related options.

Имя маршрутаRoute Name

В следующем коде определяется имя маршрутаProducts_List:The following code defines a route name of Products_List:

public class ProductsApiController : Controller
{
   [HttpGet("/products/{id}", Name = "Products_List")]
   public IActionResult GetProduct(int id) { ... }
}

Имена маршрутов могут использоваться для формирования URL-адреса на основе определенного маршрута.Route names can be used to generate a URL based on a specific route. Они не влияют на то, как производится сопоставление URL-адресов при маршрутизации, и служат только для формирования URL-адресов.Route names have no impact on the URL matching behavior of routing and are only used for URL generation. Имена маршрутов должны быть уникальными в пределах приложения.Route names must be unique application-wide.

Примечание

Сравните это поведение с маршрутом по умолчанию на основе соглашения, в котором параметр id определяется как необязательный ({id?}).Contrast this with the conventional default route, which defines the id parameter as optional ({id?}). Такая возможность точно указывать интерфейсы API имеет преимущества. Например, она позволяет направлять /products и /products/5 в разные действия.This ability to precisely specify APIs has advantages, such as allowing /products and /products/5 to be dispatched to different actions.

Объединение маршрутовCombining routes

Чтобы избежать лишних повторов при маршрутизации с помощью атрибутов, атрибуты маршрута для контроллера объединяются с атрибутами маршрута для отдельных действий.To make attribute routing less repetitive, route attributes on the controller are combined with route attributes on the individual actions. Все шаблоны маршрутов, определенные в контроллере, добавляются перед шаблонами маршрутов для действий.Any route templates defined on the controller are prepended to route templates on the actions. В результате добавления атрибута маршрута для контроллера все его действия будут использовать маршрутизацию с помощью атрибутов.Placing a route attribute on the controller makes all actions in the controller use attribute routing.

[Route("products")]
public class ProductsApiController : Controller
{
   [HttpGet]
   public IActionResult ListProducts() { ... }

   [HttpGet("{id}")]
   public ActionResult GetProduct(int id) { ... }
}

В этом примере путь URL-адреса /products может соответствовать ProductsApi.ListProducts, а путь URL-адреса /products/5 — ProductsApi.GetProduct(int).In this example the URL path /products can match ProductsApi.ListProducts, and the URL path /products/5 can match ProductsApi.GetProduct(int). Оба эти действия соответствуют только HTTP-запросу GET, так как они помечены атрибутом HttpGetAttribute.Both of these actions only match HTTP GET because they're marked with the HttpGetAttribute.

Шаблоны маршрутов, применяемые к действию, которое начинается с символа / или ~/, не объединяются с шаблонами маршрутов, применяемыми к контроллеру.Route templates applied to an action that begin with / or ~/ don't get combined with route templates applied to the controller. Этот пример соответствует такому же набору путей URL-адресов, что и маршрут по умолчанию.This example matches a set of URL paths similar to the default route.

[Route("Home")]
public class HomeController : Controller
{
    [Route("")]      // Combines to define the route template "Home"
    [Route("Index")] // Combines to define the route template "Home/Index"
    [Route("/")]     // Doesn't combine, defines the route template ""
    public IActionResult Index()
    {
        ViewData["Message"] = "Home index";
        var url = Url.Action("Index", "Home");
        ViewData["Message"] = "Home index" + "var url = Url.Action; =  " + url;
        return View();
    }

    [Route("About")] // Combines to define the route template "Home/About"
    public IActionResult About()
    {
        return View();
    }   
}

Упорядочение маршрутов на основе атрибутовOrdering attribute routes

В отличие от обычных маршрутов, которые выполняются в определенном порядке, маршрутизация атрибутов создает дерево и сопоставляет все маршруты одновременно.In contrast to conventional routes, which execute in a defined order, attribute routing builds a tree and matches all routes simultaneously. Это равносильно тому, как если бы записи маршрутов находились в идеальном порядке: наиболее конкретные маршруты имеют возможность выполнения перед более общими.This behaves as-if the route entries were placed in an ideal ordering; the most specific routes have a chance to execute before the more general routes.

Например, такой маршрут, как blog/search/{topic}, является более конкретным по сравнению с blog/{*article}.For example, a route like blog/search/{topic} is more specific than a route like blog/{*article}. С логической точки зрения, маршрут blog/search/{topic} по умолчанию выполняется первым, так как это единственная разумная очередность.Logically speaking the blog/search/{topic} route 'runs' first, by default, because that's the only sensible ordering. При использовании маршрутизации на основе соглашений разработчик отвечает за расположение маршрутов в нужном порядке.Using conventional routing, the developer is responsible for placing routes in the desired order.

При маршрутизации с помощью атрибутов порядок может настраиваться с помощью свойства Order всех атрибутов маршрутов, предоставляемых платформой.Attribute routes can configure an order, using the Order property of all of the framework provided route attributes. Маршруты обрабатываются в порядке возрастания значения свойства Order.Routes are processed according to an ascending sort of the Order property. Порядок по умолчанию — 0.The default order is 0. Маршрут, для которого задано значение Order = -1, будет выполняться перед маршрутами, для которых порядок не задан.Setting a route using Order = -1 will run before routes that don't set an order. Маршрут, для которого задано значение Order = 1, будет выполняться после маршрутов с порядком по умолчанию.Setting a route using Order = 1 will run after default route ordering.

Совет

Старайтесь не использовать свойство Order.Avoid depending on Order. Если для правильной маршрутизации в пространстве URL-адресов требуются явно заданные значения порядка, скорее всего, это будет вызывать путаницу и в среде клиентов.If your URL-space requires explicit order values to route correctly, then it's likely confusing to clients as well. Как правило, при маршрутизации с помощью атрибутов правильный маршрут выбирается посредством сопоставления URL-адресов.In general attribute routing will select the correct route with URL matching. Если порядок по умолчанию для формирования URL-адресов не работает, использовать имя маршрута в качестве переопределения, как правило, проще, чем применять свойство Order.If the default order used for URL generation isn't working, using route name as an override is usually simpler than applying the Order property.

Средства маршрутизации в Razor Pages и контроллере MVC имеют общую реализацию.Razor Pages routing and MVC controller routing share an implementation. Сведения о порядке маршрутизации в Razor Pages см. в статье Razor Pages route and app conventions: Route order (Маршрутизация и соглашения в приложении Razor Pages: порядок маршрутизации).Information on route order in the Razor Pages topics is available at Razor Pages route and app conventions: Route order.

Замена токенов в шаблонах маршрутов ([controller], [action], [area])Token replacement in route templates ([controller], [action], [area])

Для удобства маршрута на основе атрибутов поддерживают замену токенов путем заключения токена в квадратные скобки ([, ]).For convenience, attribute routes support token replacement by enclosing a token in square-braces ([, ]). Токены [action], [area] и [controller] заменяются значениями имени действия, имени области и имени контроллера из действия, в котором определен маршрут.The tokens [action], [area], and [controller] are replaced with the values of the action name, area name, and controller name from the action where the route is defined. В следующем примере действия могут соответствовать путям URL-адресов, как описано в комментариях:In the following example, the actions match URL paths as described in the comments:

[Route("[controller]/[action]")]
public class ProductsController : Controller
{
    [HttpGet] // Matches '/Products/List'
    public IActionResult List() {
        // ...
    }

    [HttpGet("{id}")] // Matches '/Products/Edit/{id}'
    public IActionResult Edit(int id) {
        // ...
    }
}

Замена токенов происходит на последнем этапе создания маршрутов на основе атрибутов.Token replacement occurs as the last step of building the attribute routes. Приведенный выше пример будет работать так же, как следующий код:The above example will behave the same as the following code:


public class ProductsController : Controller
{
    [HttpGet("[controller]/[action]")] // Matches '/Products/List'
    public IActionResult List() {
        // ...
    }

    [HttpGet("[controller]/[action]/{id}")] // Matches '/Products/Edit/{id}'
    public IActionResult Edit(int id) {
        // ...
    }
}

Маршруты на основе атрибутов могут также сочетаться с наследованием.Attribute routes can also be combined with inheritance. Эта возможность особенно эффективна при использовании вместе с заменой токенов.This is particularly powerful combined with token replacement.

[Route("api/[controller]")]
public abstract class MyBaseController : Controller { ... }

public class ProductsController : MyBaseController
{
   [HttpGet] // Matches '/api/Products'
   public IActionResult List() { ... }

   [HttpPut("{id}")] // Matches '/api/Products/{id}'
   public IActionResult Edit(int id) { ... }
}

Замена токенов также применяется к именам маршрутов, определенным в маршрутах на основе атрибутов.Token replacement also applies to route names defined by attribute routes. [Route("[controller]/[action]", Name="[controller]_[action]")] создает уникальное имя маршрута для каждого действия.[Route("[controller]/[action]", Name="[controller]_[action]")] generates a unique route name for each action.

Для сопоставления с литеральным разделителем замены токенов [ или ] его следует экранировать путем повтора символа ([[ или ]]).To match the literal token replacement delimiter [ or ], escape it by repeating the character ([[ or ]]).

Использование преобразователя параметров для настройки замены токеновUse a parameter transformer to customize token replacement

Замену токенов можно настроить, используя преобразователь параметров.Token replacement can be customized using a parameter transformer. Преобразователь параметров реализует IOutboundParameterTransformer и преобразует значения параметров.A parameter transformer implements IOutboundParameterTransformer and transforms the value of parameters. Например, пользовательский преобразователь параметра SlugifyParameterTransformer изменяет значение маршрута SubscriptionManagement на subscription-management.For example, a custom SlugifyParameterTransformer parameter transformer changes the SubscriptionManagement route value to subscription-management.

RouteTokenTransformerConvention является соглашением для модели приложения, которое:The RouteTokenTransformerConvention is an application model convention that:

  • Применяет преобразователь параметров ко всем маршрутам атрибута в приложении.Applies a parameter transformer to all attribute routes in an application.
  • Настраивает значения токена маршрут атрибута при замене.Customizes the attribute route token values as they are replaced.
public class SubscriptionManagementController : Controller
{
    [HttpGet("[controller]/[action]")] // Matches '/subscription-management/list-all'
    public IActionResult ListAll() { ... }
}

RouteTokenTransformerConvention регистрируется в качестве параметра в ConfigureServices.The RouteTokenTransformerConvention is registered as an option in ConfigureServices.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.Conventions.Add(new RouteTokenTransformerConvention(
                                     new SlugifyParameterTransformer()));
    });
}

public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
    public string TransformOutbound(object value)
    {
        if (value == null) { return null; }

        // Slugify value
        return Regex.Replace(value.ToString(), "([a-z])([A-Z])", "$1-$2").ToLower();
    }
}

Несколько маршрутовMultiple Routes

Маршрутизация с помощью атрибутов поддерживает определение нескольких маршрутов к одному и тому же действию.Attribute routing supports defining multiple routes that reach the same action. Наиболее распространенный случай использования этой возможности — имитация поведения маршрута по умолчанию на основе соглашения, как показано в следующем примере:The most common usage of this is to mimic the behavior of the default conventional route as shown in the following example:

[Route("[controller]")]
public class ProductsController : Controller
{
   [Route("")]     // Matches 'Products'
   [Route("Index")] // Matches 'Products/Index'
   public IActionResult Index()
}

Добавление нескольких атрибутов маршрута для контроллера означает, что каждый из них будет объединяться с каждым из атрибутов маршрута, определенных для методов действий.Putting multiple route attributes on the controller means that each one will combine with each of the route attributes on the action methods.

[Route("Store")]
[Route("[controller]")]
public class ProductsController : Controller
{
   [HttpPost("Buy")]     // Matches 'Products/Buy' and 'Store/Buy'
   [HttpPost("Checkout")] // Matches 'Products/Checkout' and 'Store/Checkout'
   public IActionResult Buy()
}

Если несколько атрибутов маршрута (реализующих интерфейс IActionConstraint) добавлены для действия, каждое ограничение действия объединяется с шаблоном маршрута из атрибута, в котором оно определено.When multiple route attributes (that implement IActionConstraint) are placed on an action, then each action constraint combines with the route template from the attribute that defined it.

[Route("api/[controller]")]
public class ProductsController : Controller
{
   [HttpPut("Buy")]      // Matches PUT 'api/Products/Buy'
   [HttpPost("Checkout")] // Matches POST 'api/Products/Checkout'
   public IActionResult Buy()
}

Совет

Хотя использование нескольких маршрутов к действиям может показаться очень эффективной возможностью, лучше, чтобы пространство URL-адресов приложения оставалось простым и четко организованным.While using multiple routes on actions can seem powerful, it's better to keep your application's URL space simple and well-defined. Используйте несколько маршрутов к действиям только там, где это необходимо, например для поддержки существующих клиентов.Use multiple routes on actions only where needed, for example to support existing clients.

Задание необязательных параметров, значений по умолчанию и ограничений для маршрутов на основе атрибутовSpecifying attribute route optional parameters, default values, and constraints

Маршруты на основе атрибутов поддерживают тот же синтаксис для указания необязательных параметров, значений по умолчанию и ограничений, что и маршруты на основе соглашений.Attribute routes support the same inline syntax as conventional routes to specify optional parameters, default values, and constraints.

[HttpPost("product/{id:int}")]
public IActionResult ShowProduct(int id)
{
   // ...
}

Подробное описание синтаксиса шаблона маршрута см. в разделе Справочник по шаблону маршрута.See Route Template Reference for a detailed description of route template syntax.

Пользовательские атрибуты маршрута с использованием IRouteTemplateProviderCustom route attributes using IRouteTemplateProvider

Все атрибуты маршрутов, предоставляемые платформой ( [Route(...)], [HttpGet(...)] и т. д.), реализуют интерфейс IRouteTemplateProvider.All of the route attributes provided in the framework ( [Route(...)], [HttpGet(...)] , etc.) implement the IRouteTemplateProvider interface. MVC ищет атрибуты в классах контроллеров и методах действий при запуске приложения и использует те из них, которые реализуют интерфейс IRouteTemplateProvider, для формирования начального набора маршрутов.MVC looks for attributes on controller classes and action methods when the app starts and uses the ones that implement IRouteTemplateProvider to build the initial set of routes.

Вы можете реализовать интерфейс IRouteTemplateProvider для определения собственных атрибутов маршрутов.You can implement IRouteTemplateProvider to define your own route attributes. Каждая реализация IRouteTemplateProvider позволяет определить один маршрут с пользовательским шаблоном маршрута, порядком и именем.Each IRouteTemplateProvider allows you to define a single route with a custom route template, order, and name:

public class MyApiControllerAttribute : Attribute, IRouteTemplateProvider
{
   public string Template => "api/[controller]";

   public int? Order { get; set; }

   public string Name { get; set; }
}

Атрибут в приведенном выше примере автоматически присваивает шаблону Template значение "api/[controller]" при применении [MyApiController].The attribute from the above example automatically sets the Template to "api/[controller]" when [MyApiController] is applied.

Настройка маршрутов на основе атрибутов с помощью модели приложенияUsing Application Model to customize attribute routes

Модель приложения — это объектная модель, которая создается при запуске со всеми метаданными, используемыми платформой MVC для маршрутизации и выполнения действий.The application model is an object model created at startup with all of the metadata used by MVC to route and execute your actions. Модель приложения включает в себя все данные, собранные из атрибутов маршрутов (посредством интерфейса IRouteTemplateProvider).The application model includes all of the data gathered from route attributes (through IRouteTemplateProvider). Вы можете создать соглашения, чтобы изменить модель приложения при запуске с целью настройки маршрутизации.You can write conventions to modify the application model at startup time to customize how routing behaves. В этом разделе приводится простой пример настройки маршрутизации с помощью модели приложения.This section shows a simple example of customizing routing using application model.

using Microsoft.AspNetCore.Mvc.ApplicationModels;
using System.Linq;
using System.Text;
public class NamespaceRoutingConvention : IControllerModelConvention
{
    private readonly string _baseNamespace;

    public NamespaceRoutingConvention(string baseNamespace)
    {
        _baseNamespace = baseNamespace;
    }

    public void Apply(ControllerModel controller)
    {
        var hasRouteAttributes = controller.Selectors.Any(selector =>
                                                selector.AttributeRouteModel != null);
        if (hasRouteAttributes)
        {
            // This controller manually defined some routes, so treat this 
            // as an override and not apply the convention here.
            return;
        }

        // Use the namespace and controller name to infer a route for the controller.
        //
        // Example:
        //
        //  controller.ControllerTypeInfo ->    "My.Application.Admin.UsersController"
        //  baseNamespace ->                    "My.Application"
        //
        //  template =>                         "Admin/[controller]"
        //
        // This makes your routes roughly line up with the folder structure of your project.
        //
        var namespc = controller.ControllerType.Namespace;
        if (namespc == null)
            return;
        var template = new StringBuilder();
        template.Append(namespc, _baseNamespace.Length + 1,
                        namespc.Length - _baseNamespace.Length - 1);
        template.Replace('.', '/');
        template.Append("/[controller]");

        foreach (var selector in controller.Selectors)
        {
            selector.AttributeRouteModel = new AttributeRouteModel()
            {
                Template = template.ToString()
            };
        }
    }
}

Смешанная маршрутизация с помощью атрибутов и на основе соглашенийMixed routing: Attribute routing vs conventional routing

В приложениях MVC маршрутизация с помощью атрибутов и маршрутизация на основе соглашений могут использоваться вместе.MVC applications can mix the use of conventional routing and attribute routing. Маршруты на основе соглашений часто применяются для контроллеров, предоставляющих HTML-страницы для браузеров, а маршруты на основе атрибутов — для контроллеров, предоставляющих интерфейсы REST API.It's typical to use conventional routes for controllers serving HTML pages for browsers, and attribute routing for controllers serving REST APIs.

Действия маршрутизируются либо на основе соглашений, либо с помощью атрибутов.Actions are either conventionally routed or attribute routed. При добавлении маршрута к контроллеру или действию они становятся маршрутизируемыми с помощью атрибутов.Placing a route on the controller or the action makes it attribute routed. Действия, определяющие маршруты на основе атрибутов, недоступны по маршрутам на основе соглашений, и наоборот.Actions that define attribute routes cannot be reached through the conventional routes and vice-versa. Любой атрибут маршрута контроллера делает все действия в атрибуте контроллера маршрутизируемыми.Any route attribute on the controller makes all actions in the controller attribute routed.

Примечание

Два типа систем маршрутизации отличает процесс, выполняемый после нахождения URL-адреса, соответствующего шаблону маршрута.What distinguishes the two types of routing systems is the process applied after a URL matches a route template. При маршрутизации на основе соглашений значения маршрута из соответствия используются для выбора действия и контроллера из таблицы подстановки, содержащей все действия с маршрутизацией на основе соглашений.In conventional routing, the route values from the match are used to choose the action and controller from a lookup table of all conventional routed actions. При маршрутизации с помощью атрибутов каждый шаблон уже связан с действием, и дальнейшая подстановка не требуется.In attribute routing, each template is already associated with an action, and no further lookup is needed.

Сложные сегментыComplex segments

Сложные сегменты (например, [Route("/dog{token}cat")]) обрабатываются путем "нежадного" сопоставления литералов справа налево.Complex segments (for example, [Route("/dog{token}cat")]), are processed by matching up literals from right to left in a non-greedy way. Описание см. в исходном коде.See the source code for a description. Дополнительные сведения см. в этой проблеме.For more information, see this issue.

Формирование URL-адресаURL Generation

Приложения MVC могут использовать функции формирования URL-адреса, предоставляемые системой маршрутизации, для создания URL-ссылок на действия.MVC applications can use routing's URL generation features to generate URL links to actions. Формирование URL-адресов устраняет необходимость в их жестком задании, что делает код надежнее и проще в обслуживании.Generating URLs eliminates hardcoding URLs, making your code more robust and maintainable. В этом разделе рассматриваются функции формирования URL-адреса, предоставляемые платформой MVC, и описываются лишь базовые принципы их работы.This section focuses on the URL generation features provided by MVC and will only cover basics of how URL generation works. Подробное описание формирования URL-адреса см. в статье Маршрутизация.See Routing for a detailed description of URL generation.

Интерфейс IUrlHelper — это базовый компонент инфраструктуры, обеспечивающий взаимодействие между MVC и системой маршрутизации для формирования URL-адресов.The IUrlHelper interface is the underlying piece of infrastructure between MVC and routing for URL generation. Доступ к экземпляру IUrlHelper в контроллерах, представлениях и компонентах представлений можно получить посредством свойства Url.You'll find an instance of IUrlHelper available through the Url property in controllers, views, and view components.

В этом примере интерфейс IUrlHelper используется посредством свойства Controller.Url для формирования URL-адреса другого действия.In this example, the IUrlHelper interface is used through the Controller.Url property to generate a URL to another action.

using Microsoft.AspNetCore.Mvc;

public class UrlGenerationController : Controller
{
    public IActionResult Source()
    {
        // Generates /UrlGeneration/Destination
        var url = Url.Action("Destination");
        return Content($"Go check out {url}, it's really great.");
    }

    public IActionResult Destination()
    {
        return View();
    }
}

Если в приложении применяется маршрут по умолчанию на основе соглашения, значением переменной url будет строка с путем URL-адреса /UrlGeneration/Destination.If the application is using the default conventional route, the value of the url variable will be the URL path string /UrlGeneration/Destination. Этот путь URL-адреса создается системой маршрутизации путем объединения значений маршрута из текущего запроса (значения окружения) со значениями, переданными в Url.Action, и подстановки этих значений в шаблон маршрута.This URL path is created by routing by combining the route values from the current request (ambient values), with the values passed to Url.Action and substituting those values into the route template:

ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}

result: /UrlGeneration/Destination

Значение каждого параметра маршрута в шаблоне маршрута заменяется соответствующими именами со значениями и значениями окружения.Each route parameter in the route template has its value substituted by matching names with the values and ambient values. Параметр маршрута, у которого нет значения, может принимать значение по умолчанию, если оно имеется, или пропускаться, если он является необязательным (как в случае с параметром id в этом примере).A route parameter that doesn't have a value can use a default value if it has one, or be skipped if it's optional (as in the case of id in this example). Сформировать URL-адрес не удастся, если у любого из обязательных параметров маршрута не будет соответствующего значения.URL generation will fail if any required route parameter doesn't have a corresponding value. Если для маршрута не удалось сформировать URL-адрес, проверяется следующий маршрут, пока не будут проверены все маршруты или не будет найдено соответствие.If URL generation fails for a route, the next route is tried until all routes have been tried or a match is found.

В примере Url.Action выше предполагается использование маршрутизации на основе соглашений, однако формирование URL-адреса происходит аналогичным образом и в случае с маршрутизацией с помощью атрибутов, хотя принципы иные.The example of Url.Action above assumes conventional routing, but URL generation works similarly with attribute routing, though the concepts are different. При применении маршрутизации на основе соглашений шаблон расширяется с помощью значений маршрута, и значения маршрута для controller и action обычно находятся в этом шаблоне. Это возможно по той причине, что URL-адреса, сопоставленные системой маршрутизации, следуют соглашению.With conventional routing, the route values are used to expand a template, and the route values for controller and action usually appear in that template - this works because the URLs matched by routing adhere to a convention. При маршрутизации с помощью атрибутов значения маршрута для controller и action не могут находиться в шаблоне. Вместо этого они применяются для поиска нужного шаблона.In attribute routing, the route values for controller and action are not allowed to appear in the template - they're instead used to look up which template to use.

В этом примере используется маршрутизация с помощью атрибутов:This example uses attribute routing:

// In Startup class
public void Configure(IApplicationBuilder app)
{
    app.UseMvc();
}
using Microsoft.AspNetCore.Mvc;

public class UrlGenerationController : Controller
{
    [HttpGet("")]
    public IActionResult Source()
    {
        var url = Url.Action("Destination"); // Generates /custom/url/to/destination
        return Content($"Go check out {url}, it's really great.");
    }

    [HttpGet("custom/url/to/destination")]
    public IActionResult Destination() {
        return View();
    }
}

MVC создает таблицу подстановки, содержащую все действия с маршрутизацией с помощью атрибутов, и сопоставляет значения controller и action для выбора шаблона маршрута, с помощью которого должен формироваться URL-адрес.MVC builds a lookup table of all attribute routed actions and will match the controller and action values to select the route template to use for URL generation. В приведенном выше примере создается URL-адрес custom/url/to/destination.In the sample above, custom/url/to/destination is generated.

Формирование URL-адресов по имени действияGenerating URLs by action name

Url.Action (IUrlHelper .Url.Action (IUrlHelper . Action) и все связанные перегрузки предполагают необходимость указания целевого объекта путем задания имени контроллера и имени действия.Action) and all related overloads all are based on that idea that you want to specify what you're linking to by specifying a controller name and action name.

Примечание

При использовании метода Url.Action текущие значения маршрута для controller и action уже определены: значения controller и action включены как в значения окружения, так и в обычные значения.When using Url.Action, the current route values for controller and action are specified for you - the value of controller and action are part of both ambient values and values. Метод Url.Action всегда использует текущие значения action и controller и формирует путь URL-адреса, ведущий к текущему действию.The method Url.Action, always uses the current values of action and controller and will generate a URL path that routes to the current action.

При формировании URL-адреса система маршрутизации пытается использовать значения окружения для заполнения сведений, которые вы не предоставили.Routing attempts to use the values in ambient values to fill in information that you didn't provide when generating a URL. При использовании такого маршрута, как {a}/{b}/{c}/{d}, и значений окружения { a = Alice, b = Bob, c = Carol, d = David } система маршрутизации имеет достаточно информации для формирования URL-адреса без дополнительных значений, так как все параметры маршрута имеют значения.Using a route like {a}/{b}/{c}/{d} and ambient values { a = Alice, b = Bob, c = Carol, d = David }, routing has enough information to generate a URL without any additional values - since all route parameters have a value. Если добавить значение { d = Donovan }, значение { d = David } не будет учитываться, и будет сформирован путь URL-адреса Alice/Bob/Carol/Donovan.If you added the value { d = Donovan }, the value { d = David } would be ignored, and the generated URL path would be Alice/Bob/Carol/Donovan.

Предупреждение

Пути URL-адресов являются иерархическими.URL paths are hierarchical. Если в приведенном выше примере добавить значение { c = Cheryl }, пропущены будут оба значения { c = Carol, d = David }.In the example above, if you added the value { c = Cheryl }, both of the values { c = Carol, d = David } would be ignored. В этом случае параметр d больше не имеет значения и сформировать URL-адрес не удастся.In this case we no longer have a value for d and URL generation will fail. Потребуется указать нужные значения для параметров c и d.You would need to specify the desired value of c and d. Эта проблема может возникнуть с маршрутом по умолчанию ({controller}/{action}/{id?}), но на практике она встречается редко, так как Url.Action всегда явным образом задает значения controller и action.You might expect to hit this problem with the default route ({controller}/{action}/{id?}) - but you will rarely encounter this behavior in practice as Url.Action will always explicitly specify a controller and action value.

Более длинные перегрузки Url.Action также принимают дополнительный объект значений маршрута для предоставления значений параметров маршрута, отличных от controller и action.Longer overloads of Url.Action also take an additional route values object to provide values for route parameters other than controller and action. Чаще всего он применяется с параметром id, например Url.Action("Buy", "Products", new { id = 17 }).You will most commonly see this used with id like Url.Action("Buy", "Products", new { id = 17 }). В соответствии с соглашением объект значений маршрута обычно имеет анонимный тип, но это может быть также экземпляр IDictionary<> или обычный объект .NET.By convention the route values object is usually an object of anonymous type, but it can also be an IDictionary<> or a plain old .NET object. Остальные значения маршрута, которые не соответствуют параметрам маршрута, помещаются в строку запроса.Any additional route values that don't match route parameters are put in the query string.

using Microsoft.AspNetCore.Mvc;

public class TestController : Controller
{
    public IActionResult Index()
    {
        // Generates /Products/Buy/17?color=red
        var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
        return Content(url);
    }
}

Совет

Чтобы создать абсолютный URL-адрес, используйте перегрузку, принимающую protocol: Url.Action("Buy", "Products", new { id = 17 }, protocol: Request.Scheme)To create an absolute URL, use an overload that accepts a protocol: Url.Action("Buy", "Products", new { id = 17 }, protocol: Request.Scheme)

Формирование URL-адресов по маршрутуGenerating URLs by route

В приведенном выше коде демонстрировалось формирование URL-адреса путем передачи имен контроллера и действия.The code above demonstrated generating a URL by passing in the controller and action name. Интерфейс IUrlHelper также предоставляет семейство методов Url.RouteUrl.IUrlHelper also provides the Url.RouteUrl family of methods. Эти методы похожи на Url.Action, но они не копируют текущие значения action и controller в значения маршрута.These methods are similar to Url.Action, but they don't copy the current values of action and controller to the route values. Наиболее распространенный способ их применения — указание имени определенного маршрута, который должен использоваться для формирования URL-адреса, как правило, без указания имени контроллера или действия.The most common usage is to specify a route name to use a specific route to generate the URL, generally without specifying a controller or action name.

using Microsoft.AspNetCore.Mvc;

public class UrlGenerationController : Controller
{
    [HttpGet("")]
    public IActionResult Source()
    {
        var url = Url.RouteUrl("Destination_Route"); // Generates /custom/url/to/destination
        return Content($"See {url}, it's really great.");
    }

    [HttpGet("custom/url/to/destination", Name = "Destination_Route")]
    public IActionResult Destination() {
        return View();
    }
}

Формирование URL-адресов в HTMLGenerating URLs in HTML

Интерфейс IHtmlHelper предоставляет методы HtmlHelper``Html.BeginForm и Html.ActionLink для создания элементов <form> и <a> соответственно.IHtmlHelper provides the HtmlHelper methods Html.BeginForm and Html.ActionLink to generate <form> and <a> elements respectively. Эти методы используют метод Url.Action для формирования URL-адреса и принимают одинаковые аргументы.These methods use the Url.Action method to generate a URL and they accept similar arguments. Эквивалентами методов Url.RouteUrl для HtmlHelper являются методы Html.BeginRouteForm и Html.RouteLink, которые имеют схожие функции.The Url.RouteUrl companions for HtmlHelper are Html.BeginRouteForm and Html.RouteLink which have similar functionality.

Для формирования URL-адресов используются вспомогательные функции тегов form и <a>.TagHelpers generate URLs through the form TagHelper and the <a> TagHelper. Обе они реализуются с помощью интерфейса IUrlHelper.Both of these use IUrlHelper for their implementation. Дополнительные сведения см. в статье Работа с формами.See Working with Forms for more information.

Внутри представлений интерфейс IUrlHelper доступен посредством свойства Url для особых случаев формирования URL-адресов, помимо описанных выше.Inside views, the IUrlHelper is available through the Url property for any ad-hoc URL generation not covered by the above.

Формирование URL-адресов в результатах действийGenerating URLS in Action Results

В предыдущих примерах было продемонстрировано использование интерфейса IUrlHelper в контроллере, хотя чаще всего URL-адреса формируются в контроллерах в рамках результата действия.The examples above have shown using IUrlHelper in a controller, while the most common usage in a controller is to generate a URL as part of an action result.

Базовые классы ControllerBase и Controller предоставляют удобные методы для результатов действий, ссылающихся на другое действие.The ControllerBase and Controller base classes provide convenience methods for action results that reference another action. Одним из типичных способов их применения является перенаправление после принятия входных данных пользователя.One typical usage is to redirect after accepting user input.

public IActionResult Edit(int id, Customer customer)
{
    if (ModelState.IsValid)
    {
        // Update DB with new details.
        return RedirectToAction("Index");
    }
    return View(customer);
}

Фабричные методы результатов действий по своей структуре похожи на методы интерфейса IUrlHelper.The action results factory methods follow a similar pattern to the methods on IUrlHelper.

Выделенные маршруты на основе соглашенийSpecial case for dedicated conventional routes

При маршрутизации на основе соглашений может использоваться особый тип определения маршрута — выделенный маршрут на основе соглашения.Conventional routing can use a special kind of route definition called a dedicated conventional route. В приведенном ниже примере маршрут с именем blog является выделенным маршрутом на основе соглашения.In the example below, the route named blog is a dedicated conventional route.

app.UseMvc(routes =>
{
    routes.MapRoute("blog", "blog/{*article}",
        defaults: new { controller = "Blog", action = "Article" });
    routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
});

При использовании таких определений маршрутов метод Url.Action("Index", "Home") создаст путь URL-адреса / с маршрутом default. В чем же причина?Using these route definitions, Url.Action("Index", "Home") will generate the URL path / with the default route, but why? Можно было бы предположить, что значений маршрута { controller = Home, action = Index } было бы достаточно для формирования URL-адреса с помощью blog и результатом было бы /blog?action=Index&controller=Home.You might guess the route values { controller = Home, action = Index } would be enough to generate a URL using blog, and the result would be /blog?action=Index&controller=Home.

В случае с выделенными маршрутами на основе соглашений используется особое поведение значений по умолчанию, для которых нет соответствующих параметров маршрута. Благодаря ему маршрут не может быть слишком универсальным при формировании URL-адреса.Dedicated conventional routes rely on a special behavior of default values that don't have a corresponding route parameter that prevents the route from being "too greedy" with URL generation. В этом случае значения по умолчанию — { controller = Blog, action = Article }, но параметров маршрута controller и action нет.In this case the default values are { controller = Blog, action = Article }, and neither controller nor action appears as a route parameter. Когда система маршрутизации производит формирование URL-адреса, предоставленные значения должны соответствовать значениям по умолчанию.When routing performs URL generation, the values provided must match the default values. Сформировать URL-адрес с помощью blog не удастся, так как значения { controller = Home, action = Index } не соответствуют { controller = Blog, action = Article }.URL generation using blog will fail because the values { controller = Home, action = Index } don't match { controller = Blog, action = Article }. После этого система маршрутизации выполнит попытку использовать маршрут default, которая завершится успешно.Routing then falls back to try default, which succeeds.

ЗоныAreas

Области — это возможность MVC, которая служит для объединения связанных функций в группу в виде отдельного пространства имен маршрутизации (для действий контроллеров) и структуры папок (для представлений).Areas are an MVC feature used to organize related functionality into a group as a separate routing-namespace (for controller actions) and folder structure (for views). Благодаря областям приложение может иметь несколько контроллеров с одинаковыми именами, если у них будут разные области.Using areas allows an application to have multiple controllers with the same name - as long as they have different areas. При использовании областей создается иерархия в целях маршрутизации. Для этого к area и controller добавляется еще один параметр маршрута, action.Using areas creates a hierarchy for the purpose of routing by adding another route parameter, area to controller and action. В этом разделе рассматривается взаимодействие системы маршрутизации с областями. Подробные сведения об использовании областей с представлениями см. в статье Области.This section will discuss how routing interacts with areas - see Areas for details about how areas are used with views.

В следующем примере в MVC настраивается использование маршрута на основе соглашения по умолчанию и маршрута области для области с именем Blog:The following example configures MVC to use the default conventional route and an area route for an area named Blog:

app.UseEndpoints(endpoints =>
{
    endpoints.MapAreaControllerRoute("blog_route", "Blog",
        "Manage/{controller}/{action}/{id?}");
    endpoints.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
});

Результатом сопоставления первого маршрута с таким путем URL-адреса, как /Manage/Users/AddUser, будут значения маршрута { area = Blog, controller = Users, action = AddUser }.When matching a URL path like /Manage/Users/AddUser, the first route will produce the route values { area = Blog, controller = Users, action = AddUser }. Значение маршрута area получается на основе значения по умолчанию для параметра area. По сути, маршрут, создаваемый методом MapAreaRoute, эквивалентен следующему:The area route value is produced by a default value for area, in fact the route created by MapAreaRoute is equivalent to the following:

Метод MapAreaRoute создает маршрут с помощью значения по умолчанию и ограничения для area с использованием предоставленного имени маршрута (в данном случае Blog).MapAreaRoute creates a route using both a default value and constraint for area using the provided area name, in this case Blog. Значение по умолчанию гарантирует, что маршрут всегда создает значение { area = Blog, ... }. Ограничение требует значения { area = Blog, ... } для формирования URL-адреса.The default value ensures that the route always produces { area = Blog, ... }, the constraint requires the value { area = Blog, ... } for URL generation.

Совет

При маршрутизации на основе соглашений учитывается порядок.Conventional routing is order-dependent. Как правило, маршруты с областями должны находиться раньше в таблице маршрутов, так как они более точные, чем маршруты без областей.In general, routes with areas should be placed earlier in the route table as they're more specific than routes without an area.

В предыдущем примере значения маршрута будут соответствовать следующему действию:Using the above example, the route values would match the following action:

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace1
{
    [Area("Blog")]
    public class UsersController : Controller
    {
        // GET /manage/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}

AreaAttribute обозначает контроллер в рамках области. В таком случае говорят, что контроллер находится в области Blog.The AreaAttribute is what denotes a controller as part of an area, we say that this controller is in the Blog area. Контроллеры без атрибута [Area] не входят ни в одну область и не будут соответствовать маршруту, если система маршрутизации предоставляет значение маршрута area.Controllers without an [Area] attribute are not members of any area, and will not match when the area route value is provided by routing. В приведенном ниже примере только первый контроллер может соответствовать значениям маршрута { area = Blog, controller = Users, action = AddUser }.In the following example, only the first controller listed can match the route values { area = Blog, controller = Users, action = AddUser }.

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace1
{
    [Area("Blog")]
    public class UsersController : Controller
    {
        // GET /manage/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace2
{
    // Matches { area = Zebra, controller = Users, action = AddUser }
    [Area("Zebra")]
    public class UsersController : Controller
    {
        // GET /zebra/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}
using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace3
{
    // Matches { area = string.Empty, controller = Users, action = AddUser }
    // Matches { area = null, controller = Users, action = AddUser }
    // Matches { controller = Users, action = AddUser }
    public class UsersController : Controller
    {
        // GET /users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }
    }
}

Примечание

Пространство имен каждого контроллера приведено здесь для полноты. В противном случае между контроллерами возник бы конфликт именования, и произошла бы ошибка компилятора.The namespace of each controller is shown here for completeness - otherwise the controllers would have a naming conflict and generate a compiler error. Имена пространств классов не влияют на маршрутизацию в MVC.Class namespaces have no effect on MVC's routing.

Первые два контроллера входят в области и будут соответствовать запросу, только если соответствующее имя области предоставлено значением маршрута area.The first two controllers are members of areas, and only match when their respective area name is provided by the area route value. Третий контроллер не входит ни в одну область и может соответствовать запросу, только если значение area не предоставлено системой маршрутизации.The third controller isn't a member of any area, and can only match when no value for area is provided by routing.

Примечание

В плане сопоставления отсутствующих значений отсутствие значения area равносильно тому, как если значением area было бы NULL или пустая строка.In terms of matching no value, the absence of the area value is the same as if the value for area were null or the empty string.

При выполнении действия внутри области значение маршрута для area будет доступно как значение окружения, которое система маршрутизации использует для формирования URL-адреса.When executing an action inside an area, the route value for area will be available as an ambient value for routing to use for URL generation. Это означает, что по умолчанию области являются фиксированными при формировании URL-адресов, как показано в следующем примере.This means that by default areas act sticky for URL generation as demonstrated by the following sample.

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace4
{
    [Area("Duck")]
    public class UsersController : Controller
    {
        // GET /Manage/users/GenerateURLInArea
        public IActionResult GenerateURLInArea()
        {
            // Uses the 'ambient' value of area.
            var url = Url.Action("Index", "Home");
            // Returns /Manage/Home/Index
            return Content(url);
        }

        // GET /Manage/users/GenerateURLOutsideOfArea
        public IActionResult GenerateURLOutsideOfArea()
        {
            // Uses the empty value for area.
            var url = Url.Action("Index", "Home", new { area = "" });
            // Returns /Manage
            return Content(url);
        }
    }
}

Основные сведения об интерфейсе IActionConstraintUnderstanding IActionConstraint

Примечание

В этом разделе описываются внутренние принципы работы платформы и то, как MVC выбирает действие, которое необходимо выполнить.This section is a deep-dive on framework internals and how MVC chooses an action to execute. Типичному приложению пользовательская реализация IActionConstraint не требуется.A typical application won't need a custom IActionConstraint

Скорее всего, вы уже пользовались интерфейсом IActionConstraint, даже если не знакомы с ним.You have likely already used IActionConstraint even if you're not familiar with the interface. Атрибут [HttpGet] и сходные атрибуты [Http-VERB] реализуют интерфейс IActionConstraint, чтобы ограничить выполнение метода действия.The [HttpGet] Attribute and similar [Http-VERB] attributes implement IActionConstraint in order to limit the execution of an action method.

public class ProductsController : Controller
{
    [HttpGet]
    public IActionResult Edit() { }

    public IActionResult Edit(...) { }
}

В случае с маршрутом по умолчанию на основе соглашения путь URL-адреса /Products/Edit даст значения { controller = Products, action = Edit }, которые будут соответствовать обоим приведенным здесь действиям.Assuming the default conventional route, the URL path /Products/Edit would produce the values { controller = Products, action = Edit }, which would match both of the actions shown here. В терминологии IActionConstraint можно сказать, что оба действия считаются кандидатами, так как они оба соответствуют данным маршрута.In IActionConstraint terminology we would say that both of these actions are considered candidates - as they both match the route data.

Когда метод HttpGetAttribute выполняется, он сообщает, что Edit() является соответствием для запроса GET, но не является соответствием для каких-либо иных HTTP-команд.When the HttpGetAttribute executes, it will say that Edit() is a match for GET and isn't a match for any other HTTP verb. Для действия Edit(...) ограничения не определены, поэтому оно соответствует любой HTTP-команде.The Edit(...) action doesn't have any constraints defined, and so will match any HTTP verb. Поэтому если рассматривать запрос POST, соответствием будет только Edit(...).So assuming a POST - only Edit(...) matches. Однако в случае с запросом GET соответствовать могут оба действия, хотя действие с IActionConstraint всегда считается имеющим приоритет.But, for a GET both actions can still match - however, an action with an IActionConstraint is always considered better than an action without. Таким образом, поскольку действие Edit() имеет атрибут [HttpGet], оно считается более конкретным и будет выбрано в случае соответствия обоих действий.So because Edit() has [HttpGet] it's considered more specific, and will be selected if both actions can match.

По сути, интерфейс IActionConstraint представляет собой своего рода перегрузку, но вместо перегрузки методов с одинаковыми именами он перегружает действия, соответствующие одному и тому же URL-адресу.Conceptually, IActionConstraint is a form of overloading, but instead of overloading methods with the same name, it's overloading between actions that match the same URL. При маршрутизации с помощью атрибутов также применяется интерфейс IActionConstraint, в результате чего кандидатами могут считаться действия из разных контроллеров.Attribute routing also uses IActionConstraint and can result in actions from different controllers both being considered candidates.

Реализация IActionConstraintImplementing IActionConstraint

Самый простой способ реализовать интерфейс IActionConstraint — создать класс, производный от System.Attribute, и добавить его в действия и контроллеры.The simplest way to implement an IActionConstraint is to create a class derived from System.Attribute and place it on your actions and controllers. MVC автоматически обнаруживает экземпляры IActionConstraint, применяемые как атрибуты.MVC will automatically discover any IActionConstraint that are applied as attributes. Вы можете использовать модель приложения для применения ограничений, и это, пожалуй, самый гибкий подход, так как он позволяет метапрограммировать способ их применения.You can use the application model to apply constraints, and this is probably the most flexible approach as it allows you to metaprogram how they're applied.

В следующем примере ограничение выбирает действие на основе кода страны из данных маршрута.In the following example, a constraint chooses an action based on a country code from the route data. Полный пример можно найти в GitHub.The full sample on GitHub.

public class CountrySpecificAttribute : Attribute, IActionConstraint
{
    private readonly string _countryCode;

    public CountrySpecificAttribute(string countryCode)
    {
        _countryCode = countryCode;
    }

    public int Order
    {
        get
        {
            return 0;
        }
    }

    public bool Accept(ActionConstraintContext context)
    {
        return string.Equals(
            context.RouteContext.RouteData.Values["country"].ToString(),
            _countryCode,
            StringComparison.OrdinalIgnoreCase);
    }
}

Вы отвечаете за реализацию метода Accept и определение очередности применения ограничений.You are responsible for implementing the Accept method and choosing an 'Order' for the constraint to execute. В этом случае метод Accept возвращает значение true, означающее, что действие является соответствующим при соответствии значения маршрута country.In this case, the Accept method returns true to denote the action is a match when the country route value matches. Таким образом, он позволяет возвращаться к действию без атрибута, чем отличается от метода RouteValueAttribute.This is different from a RouteValueAttribute in that it allows fallback to a non-attributed action. В примере показано, что если определено действие en-US, то для кода страны fr-FR будет выбран более общий контроллер, к которому не применяется ограничение [CountrySpecific(...)].The sample shows that if you define an en-US action then a country code like fr-FR will fall back to a more generic controller that doesn't have [CountrySpecific(...)] applied.

Свойство Order определяет, к какому этапу относится ограничение.The Order property decides which stage the constraint is part of. Ограничения действий применяются группами в соответствии со значением Order.Action constraints run in groups based on the Order. Например, все предоставляемые платформой атрибуты HTTP-методов имеют одинаковое значение Order, благодаря чему они выполняются на одном этапе.For example, all of the framework provided HTTP method attributes use the same Order value so that they run in the same stage. Чтобы реализовать требуемые политики, можно использовать любое количество этапов.You can have as many stages as you need to implement your desired policies.

Совет

Чтобы выбрать значение Order, подумайте, должно ли ограничение применяться перед выполнением HTTP-методов.To decide on a value for Order think about whether or not your constraint should be applied before HTTP methods. Чем меньше значение, тем раньше применяется ограничение.Lower numbers run first.