Маршрутизация к действиям контроллера в ASP.NET Core
Авторы: Райан Новак (Ryan Nowak), Кирк Ларкин (Kirk Larkin) и Рик Андерсон (Rick Anderson)
Примечание.
Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 9 этой статьи.
Предупреждение
Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 9 этой статьи.
Внимание
Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
В текущем выпуске см . версию .NET 9 этой статьи.
ASP.NET контроллеры Core используют ПО промежуточного слоя маршрутизации, чтобы сопоставить URL-адреса входящих запросов и сопоставить их с действиями. Шаблоны маршрутов:
- Определяются при запуске в
Program.cs
атрибутах или в атрибутах. - Описание сопоставления путей URL-адресов с действиями.
- Используются для создания URL-адресов для ссылок. Созданные ссылки обычно возвращаются в ответах.
Действия обычно маршрутивируются или перенаправляются атрибутами. Размещение маршрута на контроллере или действии делает его перенаправленным атрибутом. Дополнительные сведения см. в разделе Смешанная маршрутизация.
Этот документ:
- Объясняет взаимодействие между MVC и маршрутизацией:
- Как типичные приложения MVC используют функции маршрутизации.
- Охватывает оба:
- Обычная маршрутизация обычно используется с контроллерами и представлениями.
- Маршрутизация атрибутов , используемая с REST API. Если вы в первую очередь заинтересованы в маршрутизации для API, перейдите к разделу "Маршрутизация атрибутов для REST REST API".
- Дополнительные сведения о маршрутизации см. в разделе "Маршрутизация ".
- Ссылается на систему маршрутизации по умолчанию, называемую маршрутизацией конечных точек. Для обеспечения совместимости можно использовать контроллеры с предыдущей версией маршрутизации. Инструкции см. в руководстве по миграции 2.2-3.0.
Настройка обычного маршрута
Шаблон MVC ASP.NET Core создает обычный код маршрутизации , аналогичный следующему:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
MapControllerRoute используется для создания одного маршрута. Один маршрут называется маршрутом default
. Большинство приложений с контроллерами и представлениями используют шаблон маршрута, аналогичный маршруту default
. REST API должны использовать маршрутизацию атрибутов.
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Шаблон "{controller=Home}/{action=Index}/{id?}"
маршрута:
Соответствует URL-адресу, например
/Products/Details/5
Извлекает значения
{ controller = Products, action = Details, id = 5 }
маршрута путем маркеризации пути. Извлечение значений маршрута приводит к совпадению, если у приложения есть контроллер с именемProductsController
и действиемDetails
:public class ProductsController : Controller { public IActionResult Details(int id) { return ControllerContext.MyDisplayRouteInfo(id); } }
MyDisplayRouteInfo предоставляется пакетом NuGet Rick.Docs.Samples.RouteInfo и отображает информацию о маршруте.
/Products/Details/5
модель привязывает значениеid = 5
для заданияid
параметра5
. Дополнительные сведения см. в статье "Привязка модели".{controller=Home}
определяетсяHome
как значение по умолчаниюcontroller
.{action=Index}
определяетсяIndex
как значение по умолчаниюaction
.Символ
?
, который определяетсяid
{id?}
как необязательный.- Параметры маршрута по умолчанию и необязательные параметры необязательно должны присутствовать в пути URL-адреса для сопоставления. Подробное описание синтаксиса шаблона маршрута см. в разделе Справочник по шаблону маршрута.
Соответствует URL-пути
/
.Создает значения
{ controller = Home, action = Index }
маршрута.
Значения и controller
action
использование значений по умолчанию. id
не создает значение, так как в пути URL-адреса нет соответствующего сегмента. /
совпадает только в том случае, если существует HomeController
и Index
действие:
public class HomeController : Controller
{
public IActionResult Index() { ... }
}
Используя предыдущее определение контроллера и шаблон маршрута, HomeController.Index
действие выполняется для следующих путей URL-адресов:
/Home/Index/17
/Home/Index
/Home
/
Путь /
URL-адреса использует контроллеры и Index
действия шаблона маршрута по умолчаниюHome
. Путь /Home
URL-адреса использует действие шаблона маршрута по умолчанию Index
.
Универсальный метод MapDefaultControllerRoute:
app.MapDefaultControllerRoute();
Заменяет:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Внимание
Маршрутизация настраивается с помощью по промежуточного слоя и UseEndpoints по промежуточному UseRouting слоям. Чтобы использовать контроллеры, выполните приведенные действия.
- Вызов MapControllers перенаправленных контроллеров атрибутов.
- Вызов MapControllerRoute или MapAreaControllerRouteсопоставление как обычных контроллеров маршрутизации, так и перенаправленных атрибутов.
Приложениям обычно не требуется вызывать UseRouting
или UseEndpoints
. WebApplicationBuilder настраивает конвейер ПО промежуточного слоя, который создает программу-оболочку для ПО промежуточного слоя, добавленное в Program.cs
с использованием UseRouting
и UseEndpoints
. Подробные сведения см. в статье Маршрутизация в ASP.NET Core.
Маршрутизация на основе соглашений
Обычная маршрутизация используется с контроллерами и представлениями. Маршрут default
:
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Выше приведен пример обычного маршрута. Она называется обычной маршрутизацией, так как она устанавливает соглашение для путей URL-адресов:
- Первый сегмент пути,
{controller=Home}
сопоставляется с именем контроллера. - Второй сегмент,
{action=Index}
сопоставляется с именем действия . - Третий сегмент
{id?}
используется для необязательногоid
.{id?}
Делает?
его необязательным.id
используется для сопоставления с сущностью модели.
Используя этот default
маршрут, путь URL-адреса:
/Products/List
сопоставляется с действиемProductsController.List
./Blog/Article/17
сопоставляется сBlogController.Article
моделью и обычно привязываетid
параметр к 17.
Это сопоставление:
- Основан только на именах контроллеров и действий.
- Не основан на пространствах имен, расположениях исходного файла или параметрах метода.
Использование обычной маршрутизации с маршрутом по умолчанию позволяет создавать приложение, не создавая новый шаблон URL-адреса для каждого действия. Для приложения с действиями стиля CRUD , имеющих согласованность для URL-адресов между контроллерами:
- Помогает упростить код.
- Делает пользовательский интерфейс более предсказуемым.
Предупреждение
Приведенный id
выше код определяется как необязательный шаблон маршрута. Действия могут выполняться без дополнительного идентификатора, предоставленного в качестве части URL-адреса. Как правило, если id
опущен из URL-адреса:
id
имеет значение привязки0
модели.- Сущность не найдена в сопоставлении
id == 0
базы данных.
Маршрутизация атрибутов обеспечивает точное управление идентификатором, необходимым для некоторых действий, а не для других. По соглашению документация включает необязательные параметры, например id
, если они, скорее всего, будут отображаться в правильном использовании.
Для большинства приложений следует выбрать базовую описательную схему маршрутизации таким образом, чтобы URL-адреса были удобочитаемыми и осмысленными. Традиционный маршрут по умолчанию {controller=Home}/{action=Index}/{id?}
.
- Поддерживает основную и описательную схемы маршрутизации.
- Является отправной точкой для приложений на базе пользовательского интерфейса.
- Является единственным шаблоном маршрута, необходимым для многих веб-приложений пользовательского интерфейса. Для более крупных веб-приложений пользовательского интерфейса другой маршрут с использованием областей часто требуется.
MapControllerRoute и MapAreaRoute :
- Автоматически назначьте значение заказа конечным точкам в зависимости от того, как они вызываются.
Маршрутизация конечных точек в ASP.NET Core:
- Не имеет концепции маршрутов.
- Не предоставляет гарантии упорядочения для выполнения расширяемости, все конечные точки обрабатываются одновременно.
Чтобы увидеть, как встроенные реализации маршрутизации, такие как Route, сопоставляются с запросами, включите ведение журнала.
Маршрутизация атрибутов описана далее в этом документе.
Несколько обычных маршрутов
Можно настроить несколько обычных маршрутов, добавив в него дополнительные вызовы MapControllerRoute и MapAreaControllerRoute. Это позволяет определять несколько соглашений или добавлять обычные маршруты, предназначенные для определенного действия, например:
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Маршрут blog
в предыдущем коде — это выделенный обычный маршрут. Он называется выделенным обычным маршрутом, так как:
- Он использует обычную маршрутизацию.
- Он посвящен определенному действию.
Так как controller
и action
не отображаются в шаблоне "blog/{*article}"
маршрута в качестве параметров:
- Они могут иметь только значения
{ controller = "Blog", action = "Article" }
по умолчанию. - Этот маршрут всегда сопоставляется с действием
BlogController.Article
.
/Blog
, /Blog/Article
и /Blog/{any-string}
являются единственными URL-путями, которые соответствуют маршруту блога.
Предшествующий пример:
blog
Маршрут имеет более высокий приоритет для совпаденийdefault
, чем маршрут, так как он добавляется сначала.- Пример маршрутизации стилей Slug , где обычно имя статьи является частью URL-адреса.
Предупреждение
В ASP.NET Core маршрутизация не поддерживается:
- Определите концепцию, называемую маршрутом.
UseRouting
добавляет соответствие маршрута в конвейер ПО промежуточного слоя. ПОUseRouting
промежуточного слоя проверяет набор конечных точек, определенных в приложении, и выбирает наилучшее соответствие конечной точки на основе запроса. - Предоставьте гарантии порядка выполнения расширяемости, например IRouteConstraint или IActionConstraint.
Сведения о маршрутизации см. в справочном материале по маршрутизации.
Обычный порядок маршрутизации
Обычная маршрутизация соответствует только сочетанию действий и контроллеров, определенных приложением. Это предназначено для упрощения случаев, когда обычные маршруты перекрываются.
Добавление маршрутов с помощью MapControllerRouteи MapDefaultControllerRouteMapAreaControllerRoute автоматическое назначение значения заказа конечным точкам в зависимости от того, как они вызываются. Совпадения из маршрута, отображаемого ранее, имеют более высокий приоритет. При маршрутизации на основе соглашений учитывается порядок. Как правило, маршруты с областями должны быть размещены ранее, так как они более конкретные, чем маршруты без области. Выделенные обычные маршруты с параметрами маршрута catch-all, например {*article}
, могут сделать маршрут слишком жадным, что означает, что он соответствует URL-адресам, которые должны соответствовать другим маршрутам. Поместите жадные маршруты позже в таблицу маршрутов, чтобы предотвратить жадные совпадения.
Предупреждение
Соответствие параметра catch-all маршрутам может быть неправильным из-за ошибки в маршрутизации. Приложения, на работу которых влияет эта ошибка, обладают следующими характеристиками:
- Маршрут catch-all, например
{**slug}"
. - Маршрут catch-all не соответствует необходимым запросам.
- После удаления других маршрутов маршрут catch-all начинает функционировать должным образом.
Ознакомьтесь с примерами 18677 и 16579, в которых встречается эта ошибка, на сайте GitHub.
Опциональное исправление для этой ошибки содержится в пакете SDK для .NET Core начиная с версии 3.1.301. Следующий код задает внутренний переключатель, исправляющий эту ошибку:
public static void Main(string[] args)
{
AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior",
true);
CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.
Разрешение неоднозначных действий
Если две конечные точки соответствуют маршрутизации, маршрутизация должна выполнять одно из следующих действий:
- Выберите лучшего кандидата.
- Создание исключения.
Например:
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);
}
}
Предыдущий контроллер определяет два действия, которые соответствуют следующим:
- Путь URL-адреса
/Products33/Edit/17
- Маршрутизация данных
{ controller = Products33, action = Edit, id = 17 }
.
Это типичный шаблон для контроллеров MVC:
Edit(int)
отображает форму для изменения продукта.Edit(int, Product)
обрабатывает размещенную форму.
Чтобы устранить правильный маршрут, выполните следующие действия.
Edit(int, Product)
выбирается, когда запрос является HTTPPOST
.Edit(int)
выбирается, если http-команда — это что-либо другое.Edit(int)
обычно вызывается черезGET
.
Параметр HttpPostAttribute, [HttpPost]
предоставляется для маршрутизации, чтобы он смог выбрать в зависимости от метода HTTP запроса. Edit(int, Product)
Делает лучшее совпадениеHttpPostAttribute
, чем Edit(int)
.
Важно понимать роль атрибутов, таких как HttpPostAttribute
. Аналогичные атрибуты определяются для других http-команд. В обычной маршрутизации обычно используются те же имена действий, когда они являются частью отображаемой формы, отправки рабочего процесса формы. Например, ознакомьтесь с двумя методами действия "Изменить".
Если маршрутизация не может выбрать лучшего кандидата, AmbiguousMatchException создается исключение, перечисляя несколько сопоставленных конечных точек.
Обычные имена маршрутов
Строки и "default"
в следующих примерах "blog"
являются обычными именами маршрутов:
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Имена маршрутов дают логическое имя маршрута. Именованный маршрут можно использовать для создания URL-адресов. Использование именованного маршрута упрощает создание URL-адреса, когда порядок маршрутов может усложнять создание URL-адресов. Имена маршрутов должны быть уникальными приложениями.
Имена маршрутов:
- Не влияет на сопоставление URL-адресов или обработку запросов.
- Используются только для создания URL-адресов.
Концепция имени маршрута представлена в маршрутизации как IEndpointNameMetadata. Имя маршрута и имя конечной точки терминов:
- Взаимозаменяемы.
- Какой из них используется в документации и коде, зависит от описанного API.
Маршрутизация атрибутов для REST API
RESTAPI должны использовать маршрутизацию атрибутов для моделирования функциональных возможностей приложения в виде набора ресурсов, в которых операции представлены http-командами.
При маршрутизации с помощью атрибутов используется набор атрибутов для сопоставления действий непосредственно с шаблонами маршрутов. Следующий код является типичным REST для API и используется в следующем примере:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
В приведенном выше коде MapControllers вызывается для сопоставления перенаправленных контроллеров атрибутов.
В следующем примере :
HomeController
соответствует набору URL-адресов, аналогичных тому, что соответствует стандартному маршруту{controller=Home}/{action=Index}/{id?}
по умолчанию.
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
.
В этом примере выделена ключевая разница в программировании между маршрутизацией атрибутов и обычной маршрутизацией. Для маршрутизации атрибутов требуется больше входных данных для указания маршрута. Обычный маршрут по умолчанию обрабатывает маршруты более кратко. Однако маршрутизация атрибутов позволяет и требует точного управления шаблонами маршрутов, применяемыми к каждому действию.
При маршрутизации атрибутов имена контроллеров и действий не играют никакой роли, в которой сопоставляется действие, если не используется замена маркера. Следующий пример соответствует тем же URL-адресам, что и предыдущий пример:
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
:
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]")]
к контроллеру:
[Route("[controller]/[action]")]
public class HomeController : Controller
{
[Route("~/")]
[Route("/Home")]
[Route("~/Home/Index")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
В приведенном выше коде Index
шаблоны методов должны быть предопределены /
или ~/
шаблоны маршрутов. Шаблоны маршрутов, применяемые к действию, которое начинается с символа /
или ~/
, не объединяются с шаблонами маршрутов, применяемыми к контроллеру.
Дополнительные сведения о выборе шаблона маршрута см. в разделе "Приоритет шаблона маршрута".
Зарезервированные имена маршрутизации
Следующие ключевые слова — зарезервированные имена параметров маршрута при использовании контроллеров или Razor страниц:
action
area
controller
handler
page
Использование page
в качестве параметра маршрута с маршрутизацией атрибутов является распространенной ошибкой. Это приводит к несогласованности и запутанности поведения с созданием URL-адресов.
public class MyDemo2Controller : Controller
{
[Route("/articles/{page}")]
public IActionResult ListArticles(int page)
{
return ControllerContext.MyDisplayRouteInfo(page);
}
}
Специальные имена параметров используются поколением URL-адресов, чтобы определить, относится ли операция создания URL-адресов к Razor странице или контроллеру.
Следующие ключевые слова зарезервированы в контексте представления Razor или страницы Razor:
page
using
namespace
inject
section
inherits
model
addTagHelper
removeTagHelper
Эти ключевые слова не должны использоваться для поколений ссылок, связанных с моделью параметров или свойств верхнего уровня.
Шаблоны глаголов HTTP
ASP.NET Core имеет следующие шаблоны команд HTTP:
Шаблоны маршрутов
ASP.NET Core имеет следующие шаблоны маршрутов:
- Все шаблоны команд HTTP — это шаблоны маршрутов.
- [Route]
Маршрутизация атрибутов с атрибутами http-команд
Рассмотрим следующий контроллер:
[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);
}
}
В предыдущем коде:
- Каждое действие содержит
[HttpGet]
атрибут, который ограничивает соответствие только HTTP-запросам GET. - Действие
GetProduct
включает"{id}"
шаблон, поэтомуid
добавляется к"api/[controller]"
шаблону на контроллере. Шаблон методов ."api/[controller]/{id}"
Поэтому это действие соответствует только запросам GET для формы/api/test2/xyz
,/api/test2/123
/api/test2/{any string}
и т. д.[HttpGet("{id}")] // GET /api/test2/xyz public IActionResult GetProduct(string id) { return ControllerContext.MyDisplayRouteInfo(id); }
- Действие
GetIntProduct
содержит"int/{id:int}"
шаблон. Часть:int
шаблона ограничиваетid
значения маршрута строками, которые можно преобразовать в целое число. Запрос GET:/api/test2/int/abc
- Не соответствует этому действию.
- Возвращает ошибку 404 Not Found .
[HttpGet("int/{id:int}")] // GET /api/test2/int/3 public IActionResult GetIntProduct(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
- Действие
GetInt2Product
содержится{id}
в шаблоне, но не ограничиваетid
значения, которые можно преобразовать в целое число. Запрос GET:/api/test2/int2/abc
- Соответствует этому маршруту.
- Привязка модели не может преобразовывать
abc
в целое число. Параметрid
метода является целым числом. - Возвращает недопустимый запрос 400, так как привязка модели не удалось преобразовать
abc
в целое число.[HttpGet("int2/{id}")] // GET /api/test2/int2/3 public IActionResult GetInt2Product(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
Маршрутизация атрибутов может использовать HttpMethodAttribute такие атрибуты, как HttpPostAttribute, HttpPutAttributeи HttpDeleteAttribute. Все атрибуты команды HTTP принимают шаблон маршрута. В следующем примере показаны два действия, которые соответствуют одному шаблону маршрута:
[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);
}
}
Использование пути /products3
URL-адреса:
- Действие
MyProductsController.ListProducts
выполняется при выполненииGET
http-команды. - Действие
MyProductsController.CreateProduct
выполняется при выполненииPOST
http-команды.
При создании REST API редко необходимо использовать [Route(...)]
в методе действия, так как действие принимает все методы HTTP. Лучше использовать более конкретный атрибут HTTP-команды, чтобы быть точным в том, что поддерживает API. REST Ожидается, что клиенты API-интерфейсов будут знать, какие пути и HTTP-команды сопоставляются с конкретными логическими операциями.
REST API должны использовать маршрутизацию атрибутов для моделирования функциональных возможностей приложения в виде набора ресурсов, в которых операции представлены http-командами. Это означает, что многие операции, например GET и POST в одном логическом ресурсе, используют один и тот же URL-адрес. Маршрутизация с помощью атрибутов обеспечивает необходимый уровень контроля, позволяющий тщательно разработать схему общедоступных конечных точек API-интерфейса.
Так как маршрут на основе атрибутов применяется к определенному действию, можно легко сделать параметры обязательными в рамках определения шаблона маршрута. В следующем примере id
требуется в качестве части пути URL-адреса:
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Действие Products2ApiController.GetProduct(int)
:
- Выполняется с URL-адресом, например
/products2/3
- Не выполняется с url-адресом пути
/products2
.
Атрибут [Consumes] позволяет выполнять действие для ограничения поддерживаемых типов содержимого запросов. Дополнительные сведения см. в разделе "Определение поддерживаемых типов контента запроса" с помощью атрибута "Использование".
Полное описание шаблонов маршрутов и связанных параметров см. в статье Маршрутизация.
Дополнительные сведения см. в [ApiController]
разделе "Атрибут ApiController".
Имя маршрута
Следующий код определяет имя Products_List
маршрута:
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Имена маршрутов могут использоваться для формирования URL-адреса на основе определенного маршрута. Имена маршрутов:
- Не влияет на поведение маршрутизации по URL-адресу.
- Используются только для создания URL-адресов.
Имена маршрутов должны быть уникальными в пределах приложения.
Контрастирует предыдущий код с обычным маршрутом по умолчанию, который определяет id
параметр как необязательный ({id?}
). Возможность точно указывать API имеет преимущества, такие как разрешение /products
и /products/5
отправка в различные действия.
Объединение маршрутов атрибутов
Чтобы избежать лишних повторов при маршрутизации с помощью атрибутов, атрибуты маршрута для контроллера объединяются с атрибутами маршрута для отдельных действий. Все шаблоны маршрутов, определенные в контроллере, добавляются перед шаблонами маршрутов для действий. В результате добавления атрибута маршрута для контроллера все его действия будут использовать маршрутизацию с помощью атрибутов.
[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);
}
}
В предыдущем примере:
- Путь URL-адреса
/products
может совпадатьProductsApi.ListProducts
- Путь URL-адреса
/products/5
может совпадатьProductsApi.GetProduct(int)
.
Оба этих действия соответствуют только HTTP GET
, так как они помечены атрибутом [HttpGet]
.
Шаблоны маршрутов, применяемые к действию, которое начинается с символа /
или ~/
, не объединяются с шаблонами маршрутов, применяемыми к контроллеру. Следующий пример соответствует набору путей URL-адресов, аналогичных маршруту по умолчанию.
[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]
атрибуты в приведенном выше коде:
Атрибут | Объединяется с [Route("Home")] |
Определяет шаблон маршрута |
---|---|---|
[Route("")] |
Да | "Home" |
[Route("Index")] |
Да | "Home/Index" |
[Route("/")] |
Нет | "" |
[Route("About")] |
Да | "Home/About" |
Порядок маршрутов атрибутов
Маршрутизация создает дерево и сопоставляет все конечные точки одновременно:
- Записи маршрута ведут себя так, как будто помещаются в идеальное упорядочение.
- Наиболее конкретные маршруты имеют возможность выполняться до более общих маршрутов.
Например, маршрут атрибутов, как blog/search/{topic}
и более конкретный, чем маршрут атрибута, например blog/{*article}
. По blog/search/{topic}
умолчанию маршрут имеет более высокий приоритет, так как он более конкретный. Используя обычную маршрутизацию, разработчик отвечает за размещение маршрутов в нужном порядке.
Маршруты атрибутов могут настроить порядок с помощью Order свойства. Все предоставленные атрибуты маршрута платформы включают Order
. Маршруты обрабатываются в порядке возрастания значения свойства Order
. Порядок по умолчанию — 0
. Установка маршрута с использованием Order = -1
запусков перед маршрутами, которые не задают порядок. Настройка маршрута с помощью Order = 1
запусков после упорядочивания маршрутов по умолчанию.
Избегайте в зависимости от Order
. Если пространство URL-адресов приложения требует правильной маршрутизации явных значений порядка, скорее всего, это может запутать клиентов. Как правило, маршрутизация атрибутов выбирает правильный маршрут с сопоставлением URL-адресов. Если порядок по умолчанию, используемый для создания URL-адресов, не работает, использование имени маршрута в качестве переопределения обычно проще, чем применение Order
свойства.
Рассмотрим следующие два контроллера, которые оба определяют сопоставление /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
с приведенным выше кодом вызывает исключение, аналогичное следующему:
AmbiguousMatchException: The request matched multiple endpoints. Matches:
WebMvcRouting.Controllers.HomeController.Index
WebMvcRouting.Controllers.MyDemoController.MyIndex
Добавление Order
к одному из атрибутов маршрута разрешает неоднозначность:
[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
return ControllerContext.MyDisplayRouteInfo();
}
В приведенном выше коде /home
запускается конечная HomeController.Index
точка. Чтобы добраться до MyDemoController.MyIndex
запроса, выполните запрос /home/MyIndex
. Примечание.
- Приведенный выше код является примером или плохой структурой маршрутизации. Он использовался для иллюстрации
Order
свойства. - Свойство
Order
разрешает только неоднозначность, не удается сопоставить этот шаблон.[Route("Home")]
Лучше удалить шаблон.
См Razor . соглашения о маршрутах страниц и приложениях: порядок маршрутов для сведений о порядке маршрута с помощью Razor Pages.
В некоторых случаях ошибка HTTP 500 возвращается с неоднозначными маршрутами. Используйте ведение журнала , чтобы узнать, какие конечные точки вызвали AmbiguousMatchException
.
Замена маркеров в шаблонах маршрутов [контроллер], [действие], [область]
Для удобства маршруты атрибутов поддерживают замену маркера, заключив маркер в квадратные скобки ([
, ]
). Маркеры [action]
[area]
и [controller]
заменяются значениями имени действия, имени области и имени контроллера из действия, в котором определен маршрут:
[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);
}
}
В предыдущем коде:
[HttpGet]
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
- Спички
/Products0/List
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
- Спички
/Products0/Edit/{id}
Замена токенов происходит на последнем этапе создания маршрутов на основе атрибутов. В предыдущем примере выполняется то же самое, что и следующий код:
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, если вы хотите просмотреть комментарии кода на своем родном языке.
Маршруты на основе атрибутов могут также сочетаться с наследованием. Это мощно в сочетании с заменой маркера. Замена токенов также применяется к именам маршрутов, определенным в маршрутах на основе атрибутов.
[Route("[controller]/[action]", Name="[controller]_[action]")]
создает уникальное имя маршрута для каждого действия:
[ApiController]
[Route("api/[controller]/[action]", Name = "[controller]_[action]")]
public abstract class MyBase2Controller : ControllerBase
{
}
public class Products11Controller : MyBase2Controller
{
[HttpGet] // /api/products11/list
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")] // /api/products11/edit/3
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Для сопоставления с литеральным разделителем замены токенов [
или ]
его следует экранировать путем повтора символа ([[
или ]]
).
Использование преобразователя параметров для настройки замены токенов
Замену токенов можно настроить, используя преобразователь параметров. Преобразователь параметров реализует IOutboundParameterTransformer и преобразует значения параметров. Например, настраиваемый SlugifyParameterTransformer
преобразователь параметров изменяет значение маршрута наsubscription-management
SubscriptionManagement
:
using System.Text.RegularExpressions;
public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
public string? TransformOutbound(object? value)
{
if (value == null) { return null; }
return Regex.Replace(value.ToString()!,
"([a-z])([A-Z])",
"$1-$2",
RegexOptions.CultureInvariant,
TimeSpan.FromMilliseconds(100)).ToLowerInvariant();
}
}
RouteTokenTransformerConvention является соглашением для модели приложения, которое:
- Применяет преобразователь параметров ко всем маршрутам атрибута в приложении.
- Настраивает значения токена маршрут атрибута при замене.
public class SubscriptionManagementController : Controller
{
[HttpGet("[controller]/[action]")]
public IActionResult ListAll()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
ListAll
Предыдущий метод соответствует/subscription-management/list-all
.
Зарегистрирован RouteTokenTransformerConvention
в качестве параметра:
using Microsoft.AspNetCore.Mvc.ApplicationModels;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews(options =>
{
options.Conventions.Add(new RouteTokenTransformerConvention(
new SlugifyParameterTransformer()));
});
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Сведения об определении Slug см. в веб-документации ПО MDN.
Предупреждение
При использовании System.Text.RegularExpressions для обработки ненадежных входных данных передайте время ожидания. Злонамеренный пользователь может предоставить входные данные для RegularExpressions
, что делает возможными атаки типа "отказ в обслуживании". API платформы ASP.NET Core, использующие RegularExpressions
, передают время ожидания.
Несколько маршрутов атрибутов
Маршрутизация с помощью атрибутов поддерживает определение нескольких маршрутов к одному и тому же действию. Наиболее распространенный случай использования этой возможности — имитация поведения маршрута по умолчанию на основе соглашения, как показано в следующем примере:
[Route("[controller]")]
public class Products13Controller : Controller
{
[Route("")] // Matches 'Products13'
[Route("Index")] // Matches 'Products13/Index'
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
Размещение нескольких атрибутов маршрута на контроллере означает, что каждая из них объединяется с каждым из атрибутов маршрута в методах действия:
[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
.
При размещении нескольких атрибутов маршрута, реализующих IActionConstraint действие:
- Каждое ограничение действия объединяется с шаблоном маршрута, примененным к контроллеру.
[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-адресов вашего приложения базовым и хорошо определенным. Используйте несколько маршрутов только в тех случаях, когда это необходимо, например для поддержки существующих клиентов.
Задание необязательных параметров, значений по умолчанию и ограничений для маршрутов на основе атрибутов
Маршруты на основе атрибутов поддерживают тот же синтаксис для указания необязательных параметров, значений по умолчанию и ограничений, что и маршруты на основе соглашений.
public class Products14Controller : Controller
{
[HttpPost("product14/{id:int}")]
public IActionResult ShowProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
В приведенном выше коде [HttpPost("product14/{id:int}")]
применяется ограничение маршрута. Products14Controller.ShowProduct
Действие сопоставляется только по URL-адресам, например/product14/3
. Часть шаблона маршрута ограничивает сегмент {id:int}
только целыми числами.
Подробное описание синтаксиса шаблона маршрута см. в разделе Справочник по шаблону маршрута.
Настраиваемые атрибуты маршрута с помощью IRouteTemplateProvider
Все атрибуты маршрута реализуютIRouteTemplateProvider. Среда выполнения ASP.NET Core:
- Ищет атрибуты для классов контроллеров и методов действий при запуске приложения.
- Использует атрибуты, реализующие
IRouteTemplateProvider
для создания начального набора маршрутов.
Реализуйте для IRouteTemplateProvider
определения настраиваемых атрибутов маршрута. Каждая реализация IRouteTemplateProvider
позволяет определить один маршрут с пользовательским шаблоном маршрута, порядком и именем.
public class MyApiControllerAttribute : Attribute, IRouteTemplateProvider
{
public string Template => "api/[controller]";
public int? Order => 2;
public string Name { get; set; } = string.Empty;
}
[MyApiController]
[ApiController]
public class MyTestApiController : ControllerBase
{
// GET /api/MyTestApi
[HttpGet]
public IActionResult Get()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
Get
Предыдущий метод возвращаетOrder = 2, Template = api/MyTestApi
.
Использование модели приложения для настройки маршрутов атрибутов
Модель приложения:
- Объектная модель, созданная при запуске.
Program.cs
- Содержит все метаданные, используемые ASP.NET Core для маршрутизации и выполнения действий в приложении.
Модель приложения включает все данные, собранные из атрибутов маршрута. Данные из атрибутов маршрута предоставляются реализацией IRouteTemplateProvider
. Конвенций:
- Можно записать, чтобы изменить модель приложения, чтобы настроить поведение маршрутизации.
- Считываются при запуске приложения.
В этом разделе показан базовый пример настройки маршрутизации с помощью модели приложения. Следующий код создает маршруты примерно в соответствии со структурой папок проекта.
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
применение соглашения к контроллерам, которые маршрутивируются атрибутами:
public void Apply(ControllerModel controller)
{
var hasRouteAttributes = controller.Selectors.Any(selector =>
selector.AttributeRouteModel != null);
if (hasRouteAttributes)
{
return;
}
Например, следующий контроллер не использует 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
:
- Не делает ничего, если контроллер маршрутизируется.
- Задает шаблон контроллеров на
namespace
основе базовогоnamespace
удаления.
Его NamespaceRoutingConvention
можно применить в Program.cs
:
using My.Application.Controllers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews(options =>
{
options.Conventions.Add(
new NamespaceRoutingConvention(typeof(HomeController).Namespace!));
});
var app = builder.Build();
Например, рассмотрим следующий контроллер:
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}");
}
}
}
В предыдущем коде:
- База
namespace
—My.Application
это . - Полное имя предыдущего контроллера
My.Application.Admin.Controllers.UsersController
. - Задает
NamespaceRoutingConvention
для шаблона контроллеров значениеAdmin/Controllers/Users/[action]/{id?
.
Его NamespaceRoutingConvention
также можно применить в качестве атрибута на контроллере:
[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}");
}
}
Смешанная маршрутизация с помощью атрибутов и на основе соглашений
ASP.NET приложения Core могут использовать обычную маршрутизацию и маршрутизацию атрибутов. Обычно используются обычные маршруты для контроллеров, обслуживающих HTML-страницы для браузеров, и маршрутизация атрибутов для контроллеров, обслуживающих REST API.
Действия маршрутизируются либо на основе соглашений, либо с помощью атрибутов. При добавлении маршрута к контроллеру или действию они становятся маршрутизируемыми с помощью атрибутов. Действия, определяющие маршруты на основе атрибутов, недоступны по маршрутам на основе соглашений, и наоборот. Любой атрибут маршрута на контроллере выполняет все действия в перенаправленном атрибуте контроллера.
Маршрутизация атрибутов и обычная маршрутизация используют тот же механизм маршрутизации.
Маршрутизация со специальными символами
Маршрутизация со специальными символами может привести к непредвиденным результатам. Например, рассмотрим контроллер со следующим методом действия:
[HttpGet("{id?}/name")]
public async Task<ActionResult<string>> GetName(string id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null || todoItem.Name == null)
{
return NotFound();
}
return todoItem.Name;
}
Если string id
содержит следующие закодированные значения, могут возникнуть непредвиденные результаты:
ASCII | Encoded |
---|---|
/ |
%2F |
|
+ |
Параметры маршрута не всегда декодируются ПО URL-адресу. Эта проблема может быть решена в будущем. Дополнительные сведения см . в этой проблеме GitHub;
Создание URL-адресов и внешних значений
Приложения могут использовать функции создания URL-адресов маршрутизации для создания ссылок URL-адресов на действия. Создание URL-адресов устраняет URL-адреса жесткого кода , что делает код более надежным и поддерживающим. В этом разделе рассматриваются функции создания URL-адресов, предоставляемые MVC, и рассматриваются только основные сведения о том, как работает создание URL-адресов. Подробное описание формирования URL-адреса см. в статье Маршрутизация.
Интерфейс IUrlHelper — это базовый элемент инфраструктуры между MVC и маршрутизацией для создания URL-адресов. Экземпляр IUrlHelper
доступен через Url
свойство в контроллерах, представлениях и компонентах представления.
В следующем примере IUrlHelper
интерфейс используется через Controller.Url
свойство для создания URL-адреса для другого действия.
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
переменной — строка /UrlGeneration/Destination
пути URL-адреса. Этот путь URL-адреса создается путем объединения:
- Значения маршрута из текущего запроса, которые называются внешними значениями.
- Значения, передаваемые
Url.Action
в шаблон маршрута и подставляющие их в шаблон маршрута:
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}
result: /UrlGeneration/Destination
Значение каждого параметра маршрута в шаблоне маршрута заменяется соответствующими именами со значениями и значениями окружения. Параметр маршрута, который не имеет значения, может:
- Используйте значение по умолчанию, если оно имеет одно.
- Если это необязательно. Например,
id
из шаблона{controller}/{action}/{id?}
маршрута.
Создание URL-адресов завершается ошибкой, если какой-либо обязательный параметр маршрута не имеет соответствующего значения. Если для маршрута не удалось сформировать URL-адрес, проверяется следующий маршрут, пока не будут проверены все маршруты или не будет найдено соответствие.
В предыдущем примере Url.Action
предполагается обычная маршрутизация. Создание URL-адресов работает аналогично с маршрутизацией атрибутов, хотя основные понятия отличаются. С обычной маршрутизацией:
- Значения маршрута используются для расширения шаблона.
- Значения маршрута для
controller
иaction
обычно отображаются в этом шаблоне. Это работает, так как URL-адреса, соответствующие маршрутизации, соответствуют соглашению.
В следующем примере используется маршрутизация атрибутов:
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
.
LinkGenerator добавлен в ASP.NET Core 3.0 в качестве альтернативы IUrlHelper
. LinkGenerator
предлагает аналогичные, но более гибкие функциональные возможности. Каждый метод IUrlHelper
также имеет соответствующее семейство методов LinkGenerator
.
Формирование URL-адресов по имени действия
Url.Action, LinkGenerator.GetPathByAction и все связанные перегрузки предназначены для создания целевой конечной точки, указав имя контроллера и имя действия.
При использовании Url.Action
текущие значения маршрута для controller
и action
предоставляются средой выполнения:
- Значение и является частью как внешних значений
controller
, так иaction
значений. МетодUrl.Action
всегда использует текущие значенияaction
иcontroller
создает путь URL-адреса, который направляется к текущему действию.
Маршрутизация пытается использовать значения во внешних значениях для заполнения сведений, которые не были предоставлены при создании URL-адреса. Рассмотрим маршрут, как {a}/{b}/{c}/{d}
и с внешними значениями { a = Alice, b = Bob, c = Carol, d = David }
:
- Маршрутизация имеет достаточно сведений, чтобы создать URL-адрес без дополнительных значений.
- Маршрутизация имеет достаточно сведений, так как все параметры маршрута имеют значение.
Если добавляется значение { d = Donovan }
:
- Значение
{ d = David }
игнорируется. - Путь к созданному URL-адресу
Alice/Bob/Carol/Donovan
.
Предупреждение: пути URL-адреса являются иерархическими. В предыдущем примере, если значение { c = Cheryl }
добавляется:
- Оба значения
{ c = Carol, d = David }
игнорируются. - Больше нет значения для
d
создания URL-адресов. - Требуемые значения
c
иd
должны быть указаны для создания URL-адреса.
Может потребоваться, чтобы эта проблема была вызвана маршрутом {controller}/{action}/{id?}
по умолчанию. Эта проблема является редкой в практике, так как Url.Action
всегда явно указывает controller
и action
значение.
Несколько перегрузок Url.Action принимают объект значений маршрута, чтобы предоставить значения для параметров маршрута, отличных от controller
иaction
. Объект значений маршрута часто используется с id
. Например, Url.Action("Buy", "Products", new { id = 17 })
. Объект значений маршрута:
- По соглашению обычно является объектом анонимного типа.
- Может быть или
IDictionary<>
POCO).
Остальные значения маршрута, которые не соответствуют параметрам маршрута, помещаются в строку запроса.
public IActionResult Index()
{
var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
return Content(url!);
}
Предыдущий код создает /Products/Buy/17?color=red
.
Следующий код создает абсолютный 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-адрес, используйте одно из следующих действий:
- Перегрузка, принимаюющая объект
protocol
. Например, предыдущий код. - LinkGenerator.GetUriByAction, который создает абсолютные URI по умолчанию.
Создание URL-адресов по маршруту
Приведенный выше код демонстрирует создание URL-адреса путем передачи имени контроллера и действия. IUrlHelper
также предоставляет семейство методов Url.RouteUrl . Эти методы похожи на Url.Action, но они не копируют текущие значения action
и controller
значения маршрута. Наиболее распространенное использование Url.RouteUrl
:
- Указывает имя маршрута для создания URL-адреса.
- Как правило, не указывает имя контроллера или действия.
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
:
<h1>Test Links</h1>
<ul>
<li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>
Создание URL-адресов в HTML и Razor
IHtmlHelperHtmlHelper предоставляет методы Html.BeginForm и Html.ActionLink для создания <form>
и <a>
элементов соответственно. Эти методы используют метод Url.Action для создания URL-адреса и принимают аналогичные аргументы. Эквивалентами методов Url.RouteUrl
для HtmlHelper
являются методы Html.BeginRouteForm
и Html.RouteLink
, которые имеют схожие функции.
Для формирования URL-адресов используются вспомогательные функции тегов form
и <a>
. Обе они реализуются с помощью интерфейса IUrlHelper
. Дополнительные сведения см . в вспомогательных элементах тегов в формах .
Внутри представлений интерфейс IUrlHelper
доступен посредством свойства Url
для особых случаев формирования URL-адресов, помимо описанных выше.
Создание URL-адресов в результатах действия
В предыдущих примерах показано использование IUrlHelper
в контроллере. Наиболее распространенное использование в контроллере — создание URL-адреса в рамках результата действия.
Базовые классы ControllerBase и Controller предоставляют удобные методы для результатов действий, ссылающихся на другое действие. Одно из типичных способов использования — перенаправление после принятия входных данных пользователем:
[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
Выделенные маршруты на основе соглашений
Обычная маршрутизация может использовать специальное определение маршрута, называемое выделенным обычным маршрутом. В следующем примере имя blog
маршрута является выделенным обычным маршрутом:
app.MapControllerRoute(name: "blog",
pattern: "blog/{*article}",
defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Используя предыдущие определения маршрута, Url.Action("Index", "Home")
создается путь /
URL-адреса с помощью default
маршрута, но почему? Можно было бы предположить, что значений маршрута { controller = Home, action = Index }
было бы достаточно для формирования URL-адреса с помощью blog
и результатом было бы /blog?action=Index&controller=Home
.
Выделенные обычные маршруты зависят от специального поведения значений по умолчанию, которые не имеют соответствующего параметра маршрута, который предотвращает слишком жадный маршрут при создании URL-адресов. В этом случае значения по умолчанию — { controller = Blog, action = Article }
, но параметров маршрута controller
и action
нет. Когда система маршрутизации производит формирование URL-адреса, предоставленные значения должны соответствовать значениям по умолчанию. Создание URL-адресов сбоем blog
, так как значения { controller = Home, action = Index }
не совпадают { controller = Blog, action = Article }
. После этого система маршрутизации выполнит попытку использовать маршрут default
, которая завершится успешно.
Области
Области — это функция MVC, используемая для упорядочивания связанных функций в группу в виде отдельной:
- Пространство имен маршрутизации для действий контроллера.
- Структура папок для представлений.
Использование областей позволяет приложению иметь несколько контроллеров с одинаковым именем, если они имеют разные области. При использовании областей создается иерархия в целях маршрутизации. Для этого к controller
и action
добавляется еще один параметр маршрута, area
. В этом разделе описывается взаимодействие маршрутизации с областями. Дополнительные сведения об использовании областей с представлениями см . в разделах "Области ".
В следующем примере MVC настраивается для использования стандартного маршрута по умолчанию и area
маршрута для именованного area
Blog
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapAreaControllerRoute("blog_route", "Blog",
"Manage/{controller}/{action}/{id?}");
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
app.Run();
В приведенном выше коде MapAreaControllerRoute вызывается для создания "blog_route"
. Второй параметр — "Blog"
это имя области.
При сопоставлении URL-адреса, например /Manage/Users/AddUser
, "blog_route"
маршрут создает значения { area = Blog, controller = Users, action = AddUser }
маршрута. Значение area
маршрута создается по умолчанию.area
Маршрут, созданный MapAreaControllerRoute
следующим образом:
app.MapControllerRoute("blog_route", "Manage/{controller}/{action}/{id?}",
defaults: new { area = "Blog" }, constraints: new { area = "Blog" });
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
Метод MapAreaControllerRoute
создает маршрут с помощью значения по умолчанию и ограничения для area
с использованием предоставленного имени маршрута (в данном случае Blog
). Значение по умолчанию гарантирует, что маршрут всегда создает значение { area = Blog, ... }
. Ограничение требует значения { area = Blog, ... }
для формирования URL-адреса.
При маршрутизации на основе соглашений учитывается порядок. Как правило, маршруты с областями должны быть размещены ранее, так как они более конкретные, чем маршруты без области.
Используя предыдущий пример, значения { 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}");
}
}
}
Атрибут [Area] — это то, что обозначает контроллер как часть области. Этот контроллер находится в Blog
области. Контроллеры без [Area]
атрибута не являются членами какой-либо области и не совпадают, если area
значение маршрута предоставляется маршрутизацией. В приведенном ниже примере только первый контроллер может соответствовать значениям маршрута { 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}");
}
}
}
Пространство имен каждого контроллера отображается здесь для полноты. Если предыдущие контроллеры использовали то же пространство имен, будет создана ошибка компилятора. Имена пространств классов не влияют на маршрутизацию в MVC.
Первые два контроллера входят в области и будут соответствовать запросу, только если соответствующее имя области предоставлено значением маршрута area
. Третий контроллер не входит ни в одну область и может соответствовать запросу, только если значение area
не предоставлено системой маршрутизации.
В плане сопоставления отсутствующих значений отсутствие значения area
равносильно тому, как если значением area
было бы NULL или пустая строка.
При выполнении действия внутри области значение area
маршрута доступно в качестве внешнего значения для маршрутизации, используемой для создания URL-адресов. Это означает, что по умолчанию области являются фиксированными при формировании URL-адресов, как показано в следующем примере.
app.MapAreaControllerRoute(name: "duck_route",
areaName: "Duck",
pattern: "Manage/{controller}/{action}/{id?}");
app.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
:
public class HomeController : Controller
{
public IActionResult About()
{
var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
return Content($"URL: {url}");
}
Определение действия
Общедоступные методы на контроллере, за исключением тех, которые имеют атрибут NonAction , являются действиями.
Пример кода
- MyDisplayRouteInfo предоставляется пакетом NuGet Rick.Docs.Samples.RouteInfo и отображает информацию о маршруте.
- Просмотреть или скачать образец кода (описание загрузки)
Отладка диагностики
Для подробного вывода диагностики построения маршрутов задайте для Logging:LogLevel:Microsoft
значение Debug
. В среде разработки задайте уровень журнала в appsettings.Development.json
:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
ASP.NET контроллеры Core используют ПО промежуточного слоя маршрутизации, чтобы сопоставить URL-адреса входящих запросов и сопоставить их с действиями. Шаблоны маршрутов:
- Определены в коде запуска или атрибутах.
- Описание сопоставления путей URL-адресов с действиями.
- Используются для создания URL-адресов для ссылок. Созданные ссылки обычно возвращаются в ответах.
Действия обычно маршрутивируются или перенаправляются атрибутами. Размещение маршрута на контроллере или действии делает его перенаправленным атрибутом. Дополнительные сведения см. в разделе Смешанная маршрутизация.
Этот документ:
- Объясняет взаимодействие между MVC и маршрутизацией:
- Как типичные приложения MVC используют функции маршрутизации.
- Охватывает оба:
- Обычная маршрутизация обычно используется с контроллерами и представлениями.
- Маршрутизация атрибутов , используемая с REST API. Если вы в первую очередь заинтересованы в маршрутизации для API, перейдите к разделу "Маршрутизация атрибутов для REST REST API".
- Дополнительные сведения о маршрутизации см. в разделе "Маршрутизация ".
- Ссылается на систему маршрутизации по умолчанию, добавленную в ASP.NET Core 3.0, называемую маршрутизацией конечных точек. Для обеспечения совместимости можно использовать контроллеры с предыдущей версией маршрутизации. Инструкции см. в руководстве по миграции 2.2-3.0. Дополнительные сведения о устаревшей системе маршрутизации см. в версии 2.2 этого документа.
Настройка обычного маршрута
Startup.Configure
Обычно код аналогичен следующему при использовании обычной маршрутизации:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
Внутри вызова UseEndpointsMapControllerRoute используется для создания одного маршрута. Один маршрут называется маршрутом default
. Большинство приложений с контроллерами и представлениями используют шаблон маршрута, аналогичный маршруту default
. REST API должны использовать маршрутизацию атрибутов.
Шаблон "{controller=Home}/{action=Index}/{id?}"
маршрута:
Соответствует URL-адресу, например
/Products/Details/5
Извлекает значения
{ controller = Products, action = Details, id = 5 }
маршрута путем маркеризации пути. Извлечение значений маршрута приводит к совпадению, если у приложения есть контроллер с именемProductsController
и действиемDetails
:public class ProductsController : Controller { public IActionResult Details(int id) { return ControllerContext.MyDisplayRouteInfo(id); } }
MyDisplayRouteInfo предоставляется пакетом NuGet Rick.Docs.Samples.RouteInfo и отображает информацию о маршруте.
/Products/Details/5
модель привязывает значениеid = 5
для заданияid
параметра5
. Дополнительные сведения см. в статье "Привязка модели".{controller=Home}
определяетсяHome
как значение по умолчаниюcontroller
.{action=Index}
определяетсяIndex
как значение по умолчаниюaction
.Символ
?
, который определяетсяid
{id?}
как необязательный.Параметры маршрута по умолчанию и необязательные параметры необязательно должны присутствовать в пути URL-адреса для сопоставления. Подробное описание синтаксиса шаблона маршрута см. в разделе Справочник по шаблону маршрута.
Соответствует URL-пути
/
.Создает значения
{ controller = Home, action = Index }
маршрута.
Значения и controller
action
использование значений по умолчанию. id
не создает значение, так как в пути URL-адреса нет соответствующего сегмента. /
совпадает только в том случае, если существует HomeController
и Index
действие:
public class HomeController : Controller
{
public IActionResult Index() { ... }
}
Используя предыдущее определение контроллера и шаблон маршрута, HomeController.Index
действие выполняется для следующих путей URL-адресов:
/Home/Index/17
/Home/Index
/Home
/
Путь /
URL-адреса использует контроллеры и Index
действия шаблона маршрута по умолчаниюHome
. Путь /Home
URL-адреса использует действие шаблона маршрута по умолчанию Index
.
Универсальный метод MapDefaultControllerRoute:
endpoints.MapDefaultControllerRoute();
Заменяет:
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
Внимание
Маршрутизация настраивается с помощью по MapControllerRoute
промежуточного слоя и MapAreaControllerRoute
по промежуточному UseRoutingслоям. Чтобы использовать контроллеры, выполните приведенные действия.
- Вызов MapControllers внутри
UseEndpoints
для сопоставления перенаправленных контроллеров атрибутов. - Вызов MapControllerRoute или MapAreaControllerRouteсопоставление как обычных контроллеров маршрутизации, так и перенаправленных атрибутов.
Маршрутизация на основе соглашений
Обычная маршрутизация используется с контроллерами и представлениями. Маршрут default
:
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
Выше приведен пример обычного маршрута. Она называется обычной маршрутизацией, так как она устанавливает соглашение для путей URL-адресов:
- Первый сегмент пути,
{controller=Home}
сопоставляется с именем контроллера. - Второй сегмент,
{action=Index}
сопоставляется с именем действия . - Третий сегмент
{id?}
используется для необязательногоid
.{id?}
Делает?
его необязательным.id
используется для сопоставления с сущностью модели.
Используя этот default
маршрут, путь URL-адреса:
/Products/List
сопоставляется с действиемProductsController.List
./Blog/Article/17
сопоставляется сBlogController.Article
моделью и обычно привязываетid
параметр к 17.
Это сопоставление:
- Основан только на именах контроллеров и действий.
- Не основан на пространствах имен, расположениях исходного файла или параметрах метода.
Использование обычной маршрутизации с маршрутом по умолчанию позволяет создавать приложение, не создавая новый шаблон URL-адреса для каждого действия. Для приложения с действиями стиля CRUD , имеющих согласованность для URL-адресов между контроллерами:
- Помогает упростить код.
- Делает пользовательский интерфейс более предсказуемым.
Предупреждение
Приведенный id
выше код определяется как необязательный шаблон маршрута. Действия могут выполняться без дополнительного идентификатора, предоставленного в качестве части URL-адреса. Как правило, если id
опущен из URL-адреса:
id
имеет значение привязки0
модели.- Сущность не найдена в сопоставлении
id == 0
базы данных.
Маршрутизация атрибутов обеспечивает точное управление идентификатором, необходимым для некоторых действий, а не для других. По соглашению документация включает необязательные параметры, например id
, если они, скорее всего, будут отображаться в правильном использовании.
Для большинства приложений следует выбрать базовую описательную схему маршрутизации таким образом, чтобы URL-адреса были удобочитаемыми и осмысленными. Традиционный маршрут по умолчанию {controller=Home}/{action=Index}/{id?}
.
- Поддерживает основную и описательную схемы маршрутизации.
- Является отправной точкой для приложений на базе пользовательского интерфейса.
- Является единственным шаблоном маршрута, необходимым для многих веб-приложений пользовательского интерфейса. Для более крупных веб-приложений пользовательского интерфейса другой маршрут с использованием областей часто требуется.
MapControllerRoute и MapAreaRoute :
- Автоматически назначьте значение заказа конечным точкам в зависимости от того, как они вызываются.
Маршрутизация конечных точек в ASP.NET Core 3.0 и более поздних версий
- Не имеет концепции маршрутов.
- Не предоставляет гарантии упорядочения для выполнения расширяемости, все конечные точки обрабатываются одновременно.
Чтобы увидеть, как встроенные реализации маршрутизации, такие как Route, сопоставляются с запросами, включите ведение журнала.
Маршрутизация атрибутов описана далее в этом документе.
Несколько обычных маршрутов
Несколько обычных маршрутов можно добавить внутриUseEndpoints
, добавив в него дополнительные вызовы MapControllerRoute и MapAreaControllerRoute. Это позволяет определять несколько соглашений или добавлять обычные маршруты, предназначенные для определенного действия, например:
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
в предыдущем коде — это выделенный обычный маршрут. Он называется выделенным обычным маршрутом, так как:
- Он использует обычную маршрутизацию.
- Он посвящен определенному действию.
Так как controller
и action
не отображаются в шаблоне "blog/{*article}"
маршрута в качестве параметров:
- Они могут иметь только значения
{ controller = "Blog", action = "Article" }
по умолчанию. - Этот маршрут всегда сопоставляется с действием
BlogController.Article
.
/Blog
, /Blog/Article
и /Blog/{any-string}
являются единственными URL-путями, которые соответствуют маршруту блога.
Предшествующий пример:
blog
Маршрут имеет более высокий приоритет для совпаденийdefault
, чем маршрут, так как он добавляется сначала.- Пример маршрутизации стилей Slug , где обычно имя статьи является частью URL-адреса.
Предупреждение
В ASP.NET Core 3.0 и более поздних версий маршрутизация не поддерживается:
- Определите концепцию, называемую маршрутом.
UseRouting
добавляет соответствие маршрута в конвейер ПО промежуточного слоя. ПОUseRouting
промежуточного слоя проверяет набор конечных точек, определенных в приложении, и выбирает наилучшее соответствие конечной точки на основе запроса. - Предоставьте гарантии порядка выполнения расширяемости, например IRouteConstraint или IActionConstraint.
Сведения о маршрутизации см. в справочном материале по маршрутизации.
Обычный порядок маршрутизации
Обычная маршрутизация соответствует только сочетанию действий и контроллеров, определенных приложением. Это предназначено для упрощения случаев, когда обычные маршруты перекрываются.
Добавление маршрутов с помощью MapControllerRouteи MapDefaultControllerRouteMapAreaControllerRoute автоматическое назначение значения заказа конечным точкам в зависимости от того, как они вызываются. Совпадения из маршрута, отображаемого ранее, имеют более высокий приоритет. При маршрутизации на основе соглашений учитывается порядок. Как правило, маршруты с областями должны быть размещены ранее, так как они более конкретные, чем маршруты без области. Выделенные обычные маршруты с параметрами маршрута catch-all, например {*article}
, могут сделать маршрут слишком жадным, что означает, что он соответствует URL-адресам, которые должны соответствовать другим маршрутам. Поместите жадные маршруты позже в таблицу маршрутов, чтобы предотвратить жадные совпадения.
Предупреждение
Соответствие параметра catch-all маршрутам может быть неправильным из-за ошибки в маршрутизации. Приложения, на работу которых влияет эта ошибка, обладают следующими характеристиками:
- Маршрут catch-all, например
{**slug}"
. - Маршрут catch-all не соответствует необходимым запросам.
- После удаления других маршрутов маршрут catch-all начинает функционировать должным образом.
Ознакомьтесь с примерами 18677 и 16579, в которых встречается эта ошибка, на сайте GitHub.
Опциональное исправление для этой ошибки содержится в пакете SDK для .NET Core начиная с версии 3.1.301. Следующий код задает внутренний переключатель, исправляющий эту ошибку:
public static void Main(string[] args)
{
AppContext.SetSwitch("Microsoft.AspNetCore.Routing.UseCorrectCatchAllBehavior",
true);
CreateHostBuilder(args).Build().Run();
}
// Remaining code removed for brevity.
Разрешение неоднозначных действий
Если две конечные точки соответствуют маршрутизации, маршрутизация должна выполнять одно из следующих действий:
- Выберите лучшего кандидата.
- Создание исключения.
Например:
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);
}
}
}
Предыдущий контроллер определяет два действия, которые соответствуют следующим:
- Путь URL-адреса
/Products33/Edit/17
- Маршрутизация данных
{ controller = Products33, action = Edit, id = 17 }
.
Это типичный шаблон для контроллеров MVC:
Edit(int)
отображает форму для изменения продукта.Edit(int, Product)
обрабатывает размещенную форму.
Чтобы устранить правильный маршрут, выполните следующие действия.
Edit(int, Product)
выбирается, когда запрос является HTTPPOST
.Edit(int)
выбирается, если http-команда — это что-либо другое.Edit(int)
обычно вызывается черезGET
.
Параметр HttpPostAttribute, [HttpPost]
предоставляется для маршрутизации, чтобы он смог выбрать в зависимости от метода HTTP запроса. Edit(int, Product)
Делает лучшее совпадениеHttpPostAttribute
, чем Edit(int)
.
Важно понимать роль атрибутов, таких как HttpPostAttribute
. Аналогичные атрибуты определяются для других http-команд. В обычной маршрутизации обычно используются те же имена действий, когда они являются частью отображаемой формы, отправки рабочего процесса формы. Например, ознакомьтесь с двумя методами действия "Изменить".
Если маршрутизация не может выбрать лучшего кандидата, AmbiguousMatchException создается исключение, перечисляя несколько сопоставленных конечных точек.
Обычные имена маршрутов
Строки и "default"
в следующих примерах "blog"
являются обычными именами маршрутов:
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-адресов. Использование именованного маршрута упрощает создание URL-адреса, когда порядок маршрутов может усложнять создание URL-адресов. Имена маршрутов должны быть уникальными приложениями.
Имена маршрутов:
- Не влияет на сопоставление URL-адресов или обработку запросов.
- Используются только для создания URL-адресов.
Концепция имени маршрута представлена в маршрутизации как IEndpointNameMetadata. Имя маршрута и имя конечной точки терминов:
- Взаимозаменяемы.
- Какой из них используется в документации и коде, зависит от описанного API.
Маршрутизация атрибутов для REST API
RESTAPI должны использовать маршрутизацию атрибутов для моделирования функциональных возможностей приложения в виде набора ресурсов, в которых операции представлены http-командами.
При маршрутизации с помощью атрибутов используется набор атрибутов для сопоставления действий непосредственно с шаблонами маршрутов. Следующий StartUp.Configure
код является типичным REST для API и используется в следующем примере:
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
для сопоставления перенаправленных контроллеров атрибутов.
В следующем примере :
HomeController
соответствует набору URL-адресов, аналогичных тому, что соответствует стандартному маршруту{controller=Home}/{action=Index}/{id?}
по умолчанию.
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
.
В этом примере выделена ключевая разница в программировании между маршрутизацией атрибутов и обычной маршрутизацией. Для маршрутизации атрибутов требуется больше входных данных для указания маршрута. Обычный маршрут по умолчанию обрабатывает маршруты более кратко. Однако маршрутизация атрибутов позволяет и требует точного управления шаблонами маршрутов, применяемыми к каждому действию.
При маршрутизации атрибутов имена контроллеров и действий не играют никакой роли, в которой сопоставляется действие, если не используется замена маркера. Следующий пример соответствует тем же URL-адресам, что и предыдущий пример:
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
:
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]")]
к контроллеру:
[Route("[controller]/[action]")]
public class HomeController : Controller
{
[Route("~/")]
[Route("/Home")]
[Route("~/Home/Index")]
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
public IActionResult About()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
В приведенном выше коде Index
шаблоны методов должны быть предопределены /
или ~/
шаблоны маршрутов. Шаблоны маршрутов, применяемые к действию, которое начинается с символа /
или ~/
, не объединяются с шаблонами маршрутов, применяемыми к контроллеру.
Дополнительные сведения о выборе шаблона маршрута см. в разделе "Приоритет шаблона маршрута".
Зарезервированные имена маршрутизации
Следующие ключевые слова — зарезервированные имена параметров маршрута при использовании контроллеров или Razor страниц:
action
area
controller
handler
page
Использование page
в качестве параметра маршрута с маршрутизацией атрибутов является распространенной ошибкой. Это приводит к несогласованности и запутанности поведения с созданием URL-адресов.
public class MyDemo2Controller : Controller
{
[Route("/articles/{page}")]
public IActionResult ListArticles(int page)
{
return ControllerContext.MyDisplayRouteInfo(page);
}
}
Специальные имена параметров используются поколением URL-адресов, чтобы определить, относится ли операция создания URL-адресов к Razor странице или контроллеру.
Следующие ключевые слова зарезервированы в контексте представления Razor или страницы Razor:
page
using
namespace
inject
section
inherits
model
addTagHelper
removeTagHelper
Эти ключевые слова не должны использоваться для поколений ссылок, связанных с моделью параметров или свойств верхнего уровня.
Шаблоны глаголов HTTP
ASP.NET Core имеет следующие шаблоны команд HTTP:
Шаблоны маршрутов
ASP.NET Core имеет следующие шаблоны маршрутов:
- Все шаблоны команд HTTP — это шаблоны маршрутов.
- [Route]
Маршрутизация атрибутов с атрибутами http-команд
Рассмотрим следующий контроллер:
[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);
}
}
В предыдущем коде:
- Каждое действие содержит
[HttpGet]
атрибут, который ограничивает соответствие только HTTP-запросам GET. - Действие
GetProduct
включает"{id}"
шаблон, поэтомуid
добавляется к"api/[controller]"
шаблону на контроллере. Шаблон методов ."api/[controller]/{id}"
Поэтому это действие соответствует только запросам GET для формы/api/test2/xyz
,/api/test2/123
/api/test2/{any string}
и т. д.[HttpGet("{id}")] // GET /api/test2/xyz public IActionResult GetProduct(string id) { return ControllerContext.MyDisplayRouteInfo(id); }
- Действие
GetIntProduct
содержит"int/{id:int}"
шаблон. Часть:int
шаблона ограничиваетid
значения маршрута строками, которые можно преобразовать в целое число. Запрос GET:/api/test2/int/abc
- Не соответствует этому действию.
- Возвращает ошибку 404 Not Found .
[HttpGet("int/{id:int}")] // GET /api/test2/int/3 public IActionResult GetIntProduct(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
- Действие
GetInt2Product
содержится{id}
в шаблоне, но не ограничиваетid
значения, которые можно преобразовать в целое число. Запрос GET:/api/test2/int2/abc
- Соответствует этому маршруту.
- Привязка модели не может преобразовывать
abc
в целое число. Параметрid
метода является целым числом. - Возвращает недопустимый запрос 400, так как привязка модели не удалось преобразовать
abc
в целое число.[HttpGet("int2/{id}")] // GET /api/test2/int2/3 public IActionResult GetInt2Product(int id) { return ControllerContext.MyDisplayRouteInfo(id); }
Маршрутизация атрибутов может использовать HttpMethodAttribute такие атрибуты, как HttpPostAttribute, HttpPutAttributeи HttpDeleteAttribute. Все атрибуты команды HTTP принимают шаблон маршрута. В следующем примере показаны два действия, которые соответствуют одному шаблону маршрута:
[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);
}
}
Использование пути /products3
URL-адреса:
- Действие
MyProductsController.ListProducts
выполняется при выполненииGET
http-команды. - Действие
MyProductsController.CreateProduct
выполняется при выполненииPOST
http-команды.
При создании REST API редко необходимо использовать [Route(...)]
в методе действия, так как действие принимает все методы HTTP. Лучше использовать более конкретный атрибут HTTP-команды, чтобы быть точным в том, что поддерживает API. REST Ожидается, что клиенты API-интерфейсов будут знать, какие пути и HTTP-команды сопоставляются с конкретными логическими операциями.
REST API должны использовать маршрутизацию атрибутов для моделирования функциональных возможностей приложения в виде набора ресурсов, в которых операции представлены http-командами. Это означает, что многие операции, например GET и POST в одном логическом ресурсе, используют один и тот же URL-адрес. Маршрутизация с помощью атрибутов обеспечивает необходимый уровень контроля, позволяющий тщательно разработать схему общедоступных конечных точек API-интерфейса.
Так как маршрут на основе атрибутов применяется к определенному действию, можно легко сделать параметры обязательными в рамках определения шаблона маршрута. В следующем примере id
требуется в качестве части пути URL-адреса:
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Действие Products2ApiController.GetProduct(int)
:
- Выполняется с URL-адресом, например
/products2/3
- Не выполняется с url-адресом пути
/products2
.
Атрибут [Consumes] позволяет выполнять действие для ограничения поддерживаемых типов содержимого запросов. Дополнительные сведения см. в разделе "Определение поддерживаемых типов контента запроса" с помощью атрибута "Использование".
Полное описание шаблонов маршрутов и связанных параметров см. в статье Маршрутизация.
Дополнительные сведения см. в [ApiController]
разделе "Атрибут ApiController".
Имя маршрута
Следующий код определяет имя Products_List
маршрута:
[ApiController]
public class Products2ApiController : ControllerBase
{
[HttpGet("/products2/{id}", Name = "Products_List")]
public IActionResult GetProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Имена маршрутов могут использоваться для формирования URL-адреса на основе определенного маршрута. Имена маршрутов:
- Не влияет на поведение маршрутизации по URL-адресу.
- Используются только для создания URL-адресов.
Имена маршрутов должны быть уникальными в пределах приложения.
Контрастирует предыдущий код с обычным маршрутом по умолчанию, который определяет id
параметр как необязательный ({id?}
). Возможность точно указывать API имеет преимущества, такие как разрешение /products
и /products/5
отправка в различные действия.
Объединение маршрутов атрибутов
Чтобы избежать лишних повторов при маршрутизации с помощью атрибутов, атрибуты маршрута для контроллера объединяются с атрибутами маршрута для отдельных действий. Все шаблоны маршрутов, определенные в контроллере, добавляются перед шаблонами маршрутов для действий. В результате добавления атрибута маршрута для контроллера все его действия будут использовать маршрутизацию с помощью атрибутов.
[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);
}
}
В предыдущем примере:
- Путь URL-адреса
/products
может совпадатьProductsApi.ListProducts
- Путь URL-адреса
/products/5
может совпадатьProductsApi.GetProduct(int)
.
Оба этих действия соответствуют только HTTP GET
, так как они помечены атрибутом [HttpGet]
.
Шаблоны маршрутов, применяемые к действию, которое начинается с символа /
или ~/
, не объединяются с шаблонами маршрутов, применяемыми к контроллеру. Следующий пример соответствует набору путей URL-адресов, аналогичных маршруту по умолчанию.
[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]
атрибуты в приведенном выше коде:
Атрибут | Объединяется с [Route("Home")] |
Определяет шаблон маршрута |
---|---|---|
[Route("")] |
Да | "Home" |
[Route("Index")] |
Да | "Home/Index" |
[Route("/")] |
Нет | "" |
[Route("About")] |
Да | "Home/About" |
Порядок маршрутов атрибутов
Маршрутизация создает дерево и сопоставляет все конечные точки одновременно:
- Записи маршрута ведут себя так, как будто помещаются в идеальное упорядочение.
- Наиболее конкретные маршруты имеют возможность выполняться до более общих маршрутов.
Например, маршрут атрибутов, как blog/search/{topic}
и более конкретный, чем маршрут атрибута, например blog/{*article}
. По blog/search/{topic}
умолчанию маршрут имеет более высокий приоритет, так как он более конкретный. Используя обычную маршрутизацию, разработчик отвечает за размещение маршрутов в нужном порядке.
Маршруты атрибутов могут настроить порядок с помощью Order свойства. Все предоставленные атрибуты маршрута платформы включают Order
. Маршруты обрабатываются в порядке возрастания значения свойства Order
. Порядок по умолчанию — 0
. Установка маршрута с использованием Order = -1
запусков перед маршрутами, которые не задают порядок. Настройка маршрута с помощью Order = 1
запусков после упорядочивания маршрутов по умолчанию.
Избегайте в зависимости от Order
. Если пространство URL-адресов приложения требует правильной маршрутизации явных значений порядка, скорее всего, это может запутать клиентов. Как правило, маршрутизация атрибутов выбирает правильный маршрут с сопоставлением URL-адресов. Если порядок по умолчанию, используемый для создания URL-адресов, не работает, использование имени маршрута в качестве переопределения обычно проще, чем применение Order
свойства.
Рассмотрим следующие два контроллера, которые оба определяют сопоставление /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
с приведенным выше кодом вызывает исключение, аналогичное следующему:
AmbiguousMatchException: The request matched multiple endpoints. Matches:
WebMvcRouting.Controllers.HomeController.Index
WebMvcRouting.Controllers.MyDemoController.MyIndex
Добавление Order
к одному из атрибутов маршрута разрешает неоднозначность:
[Route("")]
[Route("Home", Order = 2)]
[Route("Home/MyIndex")]
public IActionResult MyIndex()
{
return ControllerContext.MyDisplayRouteInfo();
}
В приведенном выше коде /home
запускается конечная HomeController.Index
точка. Чтобы добраться до MyDemoController.MyIndex
запроса, выполните запрос /home/MyIndex
. Примечание.
- Приведенный выше код является примером или плохой структурой маршрутизации. Он использовался для иллюстрации
Order
свойства. - Свойство
Order
разрешает только неоднозначность, не удается сопоставить этот шаблон.[Route("Home")]
Лучше удалить шаблон.
См Razor . соглашения о маршрутах страниц и приложениях: порядок маршрутов для сведений о порядке маршрута с помощью Razor Pages.
В некоторых случаях ошибка HTTP 500 возвращается с неоднозначными маршрутами. Используйте ведение журнала , чтобы узнать, какие конечные точки вызвали AmbiguousMatchException
.
Замена маркеров в шаблонах маршрутов [контроллер], [действие], [область]
Для удобства маршруты атрибутов поддерживают замену маркера, заключив маркер в квадратные скобки ([
, ]
). Маркеры [action]
[area]
и [controller]
заменяются значениями имени действия, имени области и имени контроллера из действия, в котором определен маршрут:
[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);
}
}
В предыдущем коде:
[HttpGet]
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
- Спички
/Products0/List
[HttpGet("{id}")]
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
- Спички
/Products0/Edit/{id}
Замена токенов происходит на последнем этапе создания маршрутов на основе атрибутов. В предыдущем примере выполняется то же самое, что и следующий код:
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, если вы хотите просмотреть комментарии кода на своем родном языке.
Маршруты на основе атрибутов могут также сочетаться с наследованием. Это мощно в сочетании с заменой маркера. Замена токенов также применяется к именам маршрутов, определенным в маршрутах на основе атрибутов.
[Route("[controller]/[action]", Name="[controller]_[action]")]
создает уникальное имя маршрута для каждого действия:
[ApiController]
[Route("api/[controller]/[action]", Name = "[controller]_[action]")]
public abstract class MyBase2Controller : ControllerBase
{
}
public class Products11Controller : MyBase2Controller
{
[HttpGet] // /api/products11/list
public IActionResult List()
{
return ControllerContext.MyDisplayRouteInfo();
}
[HttpGet("{id}")] // /api/products11/edit/3
public IActionResult Edit(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
Для сопоставления с литеральным разделителем замены токенов [
или ]
его следует экранировать путем повтора символа ([[
или ]]
).
Использование преобразователя параметров для настройки замены токенов
Замену токенов можно настроить, используя преобразователь параметров. Преобразователь параметров реализует IOutboundParameterTransformer и преобразует значения параметров. Например, настраиваемый SlugifyParameterTransformer
преобразователь параметров изменяет значение маршрута наsubscription-management
SubscriptionManagement
:
public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
public string TransformOutbound(object value)
{
if (value == null) { return null; }
return Regex.Replace(value.ToString(),
"([a-z])([A-Z])",
"$1-$2",
RegexOptions.CultureInvariant,
TimeSpan.FromMilliseconds(100)).ToLowerInvariant();
}
}
RouteTokenTransformerConvention является соглашением для модели приложения, которое:
- Применяет преобразователь параметров ко всем маршрутам атрибута в приложении.
- Настраивает значения токена маршрут атрибута при замене.
public class SubscriptionManagementController : Controller
{
[HttpGet("[controller]/[action]")]
public IActionResult ListAll()
{
return ControllerContext.MyDisplayRouteInfo();
}
}
ListAll
Предыдущий метод соответствует/subscription-management/list-all
.
RouteTokenTransformerConvention
регистрируется в качестве параметра в ConfigureServices
.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Conventions.Add(new RouteTokenTransformerConvention(
new SlugifyParameterTransformer()));
});
}
Сведения об определении Slug см. в веб-документации ПО MDN.
Предупреждение
При использовании System.Text.RegularExpressions для обработки ненадежных входных данных передайте время ожидания. Злонамеренный пользователь может предоставить входные данные для RegularExpressions
, что делает возможными атаки типа "отказ в обслуживании". API платформы ASP.NET Core, использующие RegularExpressions
, передают время ожидания.
Несколько маршрутов атрибутов
Маршрутизация с помощью атрибутов поддерживает определение нескольких маршрутов к одному и тому же действию. Наиболее распространенный случай использования этой возможности — имитация поведения маршрута по умолчанию на основе соглашения, как показано в следующем примере:
[Route("[controller]")]
public class Products13Controller : Controller
{
[Route("")] // Matches 'Products13'
[Route("Index")] // Matches 'Products13/Index'
public IActionResult Index()
{
return ControllerContext.MyDisplayRouteInfo();
}
Размещение нескольких атрибутов маршрута на контроллере означает, что каждая из них объединяется с каждым из атрибутов маршрута в методах действия:
[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
.
При размещении нескольких атрибутов маршрута, реализующих IActionConstraint действие:
- Каждое ограничение действия объединяется с шаблоном маршрута, примененным к контроллеру.
[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-адресов вашего приложения базовым и хорошо определенным. Используйте несколько маршрутов только в тех случаях, когда это необходимо, например для поддержки существующих клиентов.
Задание необязательных параметров, значений по умолчанию и ограничений для маршрутов на основе атрибутов
Маршруты на основе атрибутов поддерживают тот же синтаксис для указания необязательных параметров, значений по умолчанию и ограничений, что и маршруты на основе соглашений.
public class Products14Controller : Controller
{
[HttpPost("product14/{id:int}")]
public IActionResult ShowProduct(int id)
{
return ControllerContext.MyDisplayRouteInfo(id);
}
}
В приведенном выше коде [HttpPost("product14/{id:int}")]
применяется ограничение маршрута. Products14Controller.ShowProduct
Действие сопоставляется только по URL-адресам, например/product14/3
. Часть шаблона маршрута ограничивает сегмент {id:int}
только целыми числами.
Подробное описание синтаксиса шаблона маршрута см. в разделе Справочник по шаблону маршрута.
Настраиваемые атрибуты маршрута с помощью IRouteTemplateProvider
Все атрибуты маршрута реализуютIRouteTemplateProvider. Среда выполнения ASP.NET Core:
- Ищет атрибуты для классов контроллеров и методов действий при запуске приложения.
- Использует атрибуты, реализующие
IRouteTemplateProvider
для создания начального набора маршрутов.
Реализуйте для IRouteTemplateProvider
определения настраиваемых атрибутов маршрута. Каждая реализация IRouteTemplateProvider
позволяет определить один маршрут с пользовательским шаблоном маршрута, порядком и именем.
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
.
Использование модели приложения для настройки маршрутов атрибутов
Модель приложения:
- Объектная модель, созданная при запуске.
- Содержит все метаданные, используемые ASP.NET Core для маршрутизации и выполнения действий в приложении.
Модель приложения включает все данные, собранные из атрибутов маршрута. Данные из атрибутов маршрута предоставляются реализацией IRouteTemplateProvider
. Конвенций:
- Можно записать, чтобы изменить модель приложения, чтобы настроить поведение маршрутизации.
- Считываются при запуске приложения.
В этом разделе показан базовый пример настройки маршрутизации с помощью модели приложения. Следующий код создает маршруты примерно в соответствии со структурой папок проекта.
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
применение соглашения к контроллерам, которые маршрутивируются атрибутами:
public void Apply(ControllerModel controller)
{
var hasRouteAttributes = controller.Selectors.Any(selector =>
selector.AttributeRouteModel != null);
if (hasRouteAttributes)
{
return;
}
Например, следующий контроллер не использует 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
:
- Не делает ничего, если контроллер маршрутизируется.
- Задает шаблон контроллеров на
namespace
основе базовогоnamespace
удаления.
Его NamespaceRoutingConvention
можно применить в 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.
Например, рассмотрим следующий контроллер:
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}");
}
}
}
В предыдущем коде:
- База
namespace
—My.Application
это . - Полное имя предыдущего контроллера
My.Application.Admin.Controllers.UsersController
. - Задает
NamespaceRoutingConvention
для шаблона контроллеров значениеAdmin/Controllers/Users/[action]/{id?
.
Его NamespaceRoutingConvention
также можно применить в качестве атрибута на контроллере:
[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}");
}
}
Смешанная маршрутизация с помощью атрибутов и на основе соглашений
ASP.NET приложения Core могут использовать обычную маршрутизацию и маршрутизацию атрибутов. Обычно используются обычные маршруты для контроллеров, обслуживающих HTML-страницы для браузеров, и маршрутизация атрибутов для контроллеров, обслуживающих REST API.
Действия маршрутизируются либо на основе соглашений, либо с помощью атрибутов. При добавлении маршрута к контроллеру или действию они становятся маршрутизируемыми с помощью атрибутов. Действия, определяющие маршруты на основе атрибутов, недоступны по маршрутам на основе соглашений, и наоборот. Любой атрибут маршрута на контроллере выполняет все действия в перенаправленном атрибуте контроллера.
Маршрутизация атрибутов и обычная маршрутизация используют тот же механизм маршрутизации.
Создание URL-адресов и внешних значений
Приложения могут использовать функции создания URL-адресов маршрутизации для создания ссылок URL-адресов на действия. Создание URL-адресов устраняет url-адреса жесткого кода, что делает код более надежным и поддерживающим. В этом разделе рассматриваются функции создания URL-адресов, предоставляемые MVC, и рассматриваются только основные сведения о том, как работает создание URL-адресов. Подробное описание формирования URL-адреса см. в статье Маршрутизация.
Интерфейс IUrlHelper — это базовый элемент инфраструктуры между MVC и маршрутизацией для создания URL-адресов. Экземпляр IUrlHelper
доступен через Url
свойство в контроллерах, представлениях и компонентах представления.
В следующем примере IUrlHelper
интерфейс используется через Controller.Url
свойство для создания URL-адреса для другого действия.
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
переменной — строка /UrlGeneration/Destination
пути URL-адреса. Этот путь URL-адреса создается путем объединения:
- Значения маршрута из текущего запроса, которые называются внешними значениями.
- Значения, передаваемые
Url.Action
в шаблон маршрута и подставляющие их в шаблон маршрута:
ambient values: { controller = "UrlGeneration", action = "Source" }
values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" }
route template: {controller}/{action}/{id?}
result: /UrlGeneration/Destination
Значение каждого параметра маршрута в шаблоне маршрута заменяется соответствующими именами со значениями и значениями окружения. Параметр маршрута, который не имеет значения, может:
- Используйте значение по умолчанию, если оно имеет одно.
- Если это необязательно. Например,
id
из шаблона{controller}/{action}/{id?}
маршрута.
Создание URL-адресов завершается ошибкой, если какой-либо обязательный параметр маршрута не имеет соответствующего значения. Если для маршрута не удалось сформировать URL-адрес, проверяется следующий маршрут, пока не будут проверены все маршруты или не будет найдено соответствие.
В предыдущем примере Url.Action
предполагается обычная маршрутизация. Создание URL-адресов работает аналогично с маршрутизацией атрибутов, хотя основные понятия отличаются. С обычной маршрутизацией:
- Значения маршрута используются для расширения шаблона.
- Значения маршрута для
controller
иaction
обычно отображаются в этом шаблоне. Это работает, так как URL-адреса, соответствующие маршрутизации, соответствуют соглашению.
В следующем примере используется маршрутизация атрибутов:
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
.
LinkGenerator добавлен в ASP.NET Core 3.0 в качестве альтернативы IUrlHelper
. LinkGenerator
предлагает аналогичные, но более гибкие функциональные возможности. Каждый метод IUrlHelper
также имеет соответствующее семейство методов LinkGenerator
.
Формирование URL-адресов по имени действия
Url.Action, LinkGenerator.GetPathByAction и все связанные перегрузки предназначены для создания целевой конечной точки, указав имя контроллера и имя действия.
При использовании Url.Action
текущие значения маршрута для controller
и action
предоставляются средой выполнения:
- Значение и является частью как внешних значений
controller
, так иaction
значений. МетодUrl.Action
всегда использует текущие значенияaction
иcontroller
создает путь URL-адреса, который направляется к текущему действию.
Маршрутизация пытается использовать значения во внешних значениях для заполнения сведений, которые не были предоставлены при создании URL-адреса. Рассмотрим маршрут, как {a}/{b}/{c}/{d}
и с внешними значениями { a = Alice, b = Bob, c = Carol, d = David }
:
- Маршрутизация имеет достаточно сведений, чтобы создать URL-адрес без дополнительных значений.
- Маршрутизация имеет достаточно сведений, так как все параметры маршрута имеют значение.
Если добавляется значение { d = Donovan }
:
- Значение
{ d = David }
игнорируется. - Путь к созданному URL-адресу
Alice/Bob/Carol/Donovan
.
Предупреждение: пути URL-адреса являются иерархическими. В предыдущем примере, если значение { c = Cheryl }
добавляется:
- Оба значения
{ c = Carol, d = David }
игнорируются. - Больше нет значения для
d
создания URL-адресов. - Требуемые значения
c
иd
должны быть указаны для создания URL-адреса.
Может потребоваться, чтобы эта проблема была вызвана маршрутом {controller}/{action}/{id?}
по умолчанию. Эта проблема является редкой в практике, так как Url.Action
всегда явно указывает controller
и action
значение.
Несколько перегрузок Url.Action принимают объект значений маршрута, чтобы предоставить значения для параметров маршрута, отличных от controller
иaction
. Объект значений маршрута часто используется с id
. Например, Url.Action("Buy", "Products", new { id = 17 })
. Объект значений маршрута:
- По соглашению обычно является объектом анонимного типа.
- Может быть или
IDictionary<>
POCO).
Остальные значения маршрута, которые не соответствуют параметрам маршрута, помещаются в строку запроса.
public IActionResult Index()
{
var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });
return Content(url);
}
Предыдущий код создает /Products/Buy/17?color=red
.
Следующий код создает абсолютный 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-адрес, используйте одно из следующих действий:
- Перегрузка, принимаюющая объект
protocol
. Например, предыдущий код. - LinkGenerator.GetUriByAction, который создает абсолютные URI по умолчанию.
Создание URL-адресов по маршруту
Приведенный выше код демонстрирует создание URL-адреса путем передачи имени контроллера и действия. IUrlHelper
также предоставляет семейство методов Url.RouteUrl . Эти методы похожи на Url.Action, но они не копируют текущие значения action
и controller
значения маршрута. Наиболее распространенное использование Url.RouteUrl
:
- Указывает имя маршрута для создания URL-адреса.
- Как правило, не указывает имя контроллера или действия.
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
:
<h1>Test Links</h1>
<ul>
<li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>
Создание URL-адресов в HTML и Razor
IHtmlHelperHtmlHelper предоставляет методы Html.BeginForm и Html.ActionLink для создания <form>
и <a>
элементов соответственно. Эти методы используют метод Url.Action для создания URL-адреса и принимают аналогичные аргументы. Эквивалентами методов Url.RouteUrl
для HtmlHelper
являются методы Html.BeginRouteForm
и Html.RouteLink
, которые имеют схожие функции.
Для формирования URL-адресов используются вспомогательные функции тегов form
и <a>
. Обе они реализуются с помощью интерфейса IUrlHelper
. Дополнительные сведения см . в вспомогательных элементах тегов в формах .
Внутри представлений интерфейс IUrlHelper
доступен посредством свойства Url
для особых случаев формирования URL-адресов, помимо описанных выше.
Создание URL-адресов в результатах действия
В предыдущих примерах показано использование IUrlHelper
в контроллере. Наиболее распространенное использование в контроллере — создание URL-адреса в рамках результата действия.
Базовые классы ControllerBase и Controller предоставляют удобные методы для результатов действий, ссылающихся на другое действие. Одно из типичных способов использования — перенаправление после принятия входных данных пользователем:
[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
Выделенные маршруты на основе соглашений
Обычная маршрутизация может использовать специальное определение маршрута, называемое выделенным обычным маршрутом. В следующем примере имя blog
маршрута является выделенным обычным маршрутом:
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
маршрута, но почему? Можно было бы предположить, что значений маршрута { controller = Home, action = Index }
было бы достаточно для формирования URL-адреса с помощью blog
и результатом было бы /blog?action=Index&controller=Home
.
Выделенные обычные маршруты зависят от специального поведения значений по умолчанию, которые не имеют соответствующего параметра маршрута, который предотвращает слишком жадный маршрут при создании URL-адресов. В этом случае значения по умолчанию — { controller = Blog, action = Article }
, но параметров маршрута controller
и action
нет. Когда система маршрутизации производит формирование URL-адреса, предоставленные значения должны соответствовать значениям по умолчанию. Создание URL-адресов сбоем blog
, так как значения { controller = Home, action = Index }
не совпадают { controller = Blog, action = Article }
. После этого система маршрутизации выполнит попытку использовать маршрут default
, которая завершится успешно.
Области
Области — это функция MVC, используемая для упорядочивания связанных функций в группу в виде отдельной:
- Пространство имен маршрутизации для действий контроллера.
- Структура папок для представлений.
Использование областей позволяет приложению иметь несколько контроллеров с одинаковым именем, если они имеют разные области. При использовании областей создается иерархия в целях маршрутизации. Для этого к controller
и action
добавляется еще один параметр маршрута, area
. В этом разделе описывается взаимодействие маршрутизации с областями. Дополнительные сведения об использовании областей с представлениями см . в разделах "Области ".
В следующем примере MVC настраивается для использования стандартного маршрута по умолчанию и area
маршрута для именованного area
Blog
:
app.UseEndpoints(endpoints =>
{
endpoints.MapAreaControllerRoute("blog_route", "Blog",
"Manage/{controller}/{action}/{id?}");
endpoints.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
});
В приведенном выше коде MapAreaControllerRoute вызывается для создания "blog_route"
. Второй параметр — "Blog"
это имя области.
При сопоставлении URL-адреса, например /Manage/Users/AddUser
, "blog_route"
маршрут создает значения { area = Blog, controller = Users, action = AddUser }
маршрута. Значение area
маршрута создается по умолчанию.area
Маршрут, созданный MapAreaControllerRoute
следующим образом:
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
). Значение по умолчанию гарантирует, что маршрут всегда создает значение { area = Blog, ... }
. Ограничение требует значения { area = Blog, ... }
для формирования URL-адреса.
При маршрутизации на основе соглашений учитывается порядок. Как правило, маршруты с областями должны быть размещены ранее, так как они более конкретные, чем маршруты без области.
Используя предыдущий пример, значения { 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}");
}
}
}
Атрибут [Area] — это то, что обозначает контроллер как часть области. Этот контроллер находится в Blog
области. Контроллеры без [Area]
атрибута не являются членами какой-либо области и не совпадают, если area
значение маршрута предоставляется маршрутизацией. В приведенном ниже примере только первый контроллер может соответствовать значениям маршрута { 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}");
}
}
}
Пространство имен каждого контроллера отображается здесь для полноты. Если предыдущие контроллеры использовали то же пространство имен, будет создана ошибка компилятора. Имена пространств классов не влияют на маршрутизацию в MVC.
Первые два контроллера входят в области и будут соответствовать запросу, только если соответствующее имя области предоставлено значением маршрута area
. Третий контроллер не входит ни в одну область и может соответствовать запросу, только если значение area
не предоставлено системой маршрутизации.
В плане сопоставления отсутствующих значений отсутствие значения area
равносильно тому, как если значением area
было бы NULL или пустая строка.
При выполнении действия внутри области значение area
маршрута доступно в качестве внешнего значения для маршрутизации, используемой для создания URL-адресов. Это означает, что по умолчанию области являются фиксированными при формировании URL-адресов, как показано в следующем примере.
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
:
public class HomeController : Controller
{
public IActionResult About()
{
var url = Url.Action("AddUser", "Users", new { Area = "Zebra" });
return Content($"URL: {url}");
}
Определение действия
Общедоступные методы на контроллере, за исключением тех, которые имеют атрибут NonAction , являются действиями.
Пример кода
- MyDisplayRouteInfo предоставляется пакетом NuGet Rick.Docs.Samples.RouteInfo и отображает информацию о маршруте.
- Просмотреть или скачать образец кода (описание загрузки)
Отладка диагностики
Для подробного вывода диагностики построения маршрутов задайте для Logging:LogLevel:Microsoft
значение Debug
. В среде разработки задайте уровень журнала в appsettings.Development.json
:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Debug",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
ASP.NET Core