Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7
Фреймворк MVC позволяет использовать в приложении маршрутизацию на основе атрибутов. Такой тип маршрутизации еще называется Attribute-Based Routing. Атрибуты предоставляют более гибкий способ определения маршрутов. Маршруты, определенные с помощью атрибутов, имеют приоритет по сравнению с маршрутами, определенными в классе Startup.
Маршрутизация на основе атрибутов может применяться независимо от того, какой компонент отвечает в приложении за маршрутизацию - RouterMiddleware или EndpointRoitingMiddleware+EndpointMiddleware.
Для определения маршрута, необходимо использовать атрибут [Route]:
public class HomeController : Controller { [Route("Home/Index")] public IActionResult Index() { return Content("ASP.NET Core на metanit.com"); } [Route("About")] public IActionResult About() { return Content("About site"); } }
В данном случае метод Index будет обрабатывать запросы по адресу "Home/Index", а метод About по адресу "About".
Если в проекте планируется использовать только маршрутизацию на основе атрибутов, то в классе Startup мы можем не определять никаких маршрутов.
Например, при использовании EndpointMiddleware и конечных точек можно применить метод MapControllers
:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); // нет определенных маршрутов }); }
Аналогично можно не определять маршруты и при использовании RouterMiddleware:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.UseMvc(); // нет определенных маршрутов }
Но также мы можем комбинировать способы маршрутизации. При чем здесь надо учитывать, что маршрутизация на основе атрибутов имеет больший приоритет. Например, определим следующее действие контроллера:
public class HomeController : Controller { [Route("homepage")] public IActionResult Index() { return Content("Hello ASP.NET MVC на https://metanit.com"); } }
В качестве параметра атрибут Route принимает шаблон URL, с которым будет сопоставляться запрошенный адрес. И даже если у нас определен стандартный маршрут в классе Startup:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => { // определение маршрутов endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); }
Запрос типа http://localhost:xxxx/home/index работать не будет, так как мы явно указали с помощью атрибута, что метод Index контроллера Home будет обрабатывать только запросы http://localhost:xxxx/homepage
Определение маршрутов с помощью атрибутов подчиняется тем же правилам, что определение маршрутов в классе Startup. Например, используем параметры и ограничения:
public class HomeController : Controller { [Route("{id:int}/{name:maxlength(10)}")] public IActionResult Test(int id, string name) { return Content($" id={id} | name={name}"); } }
И к такому методу мы сможем обратиться с запросом типа http://localhost:xxxx/10/lumia.
Или другой пример - применение необязательного параметра:
public class HomeController : Controller { [Route("Home/Index/{id?}")] public IActionResult Test(int? id) { if(id!=null) return Content($"Параметр id={id}"); else return Content($"Параметр id неопределен"); } }
И к такому методу мы сможем обратиться с запросом типа http://localhost:xxxx/home/index или добавить параметр http://localhost:xxxx/home/index/5.
Допустим, у нас есть несколько методов, для которых определен свой маршрут, и мы хотим, чтобы эти маршруты начинались с одного определенного префикса. Например:
public class HomeController : Controller { [Route("main/index/{name}")] public IActionResult Index(string name) { return Content(name); } [Route("main/{id:int}/{name:maxlength(10)}")] public IActionResult Test(int id, string name) { return Content($" id={id} | name={name}"); } }
Вместо того, чтобы добавлять префикс "main" к каждому маршруту, мы могли бы определить его глобально для всего контроллера:
[Route("main")] public class HomeController : Controller { [Route("index/{name}")] public IActionResult Index(string name) { return Content(name); } [Route("{id:int}/{name:maxlength(10)}")] public IActionResult Test(int id, string name) { return Content($" id={id} | name={name}"); } }
От всех параметров шаблона маршрутов в атрибутах отличаются два параметра controller и action, которые ссылаются соответственно на контроллер и его действие. При использовании их надо помещать в квадратные скобки, а не в фигурные, как другие параметры:
public class HomeController : Controller { [Route("[controller]/[action]")] public IActionResult Index() { var controller = RouteData.Values["controller"].ToString(); var action = RouteData.Values["action"].ToString(); return Content($"controller: {controller} | action: {action}"); } }
Допустимо также использование их по отдельности или добавление к ним других параметров в фигурных скобках:
[Route("[controller]")] [Route("[action]")] [Route("[controller]/[action]/{id?}")]
С помощью атрибутов можно задать несколько маршрутов для одного метода. Например:
[Route("[controller]")] public class HomeController : Controller { [Route("")] // сопоставляется с Home [Route("Index")] // сопоставляется с Home/Index public IActionResult Index() { return View(); } }
Также для контроллера можно задать сразу несколько маршрутов:
[Route("Store")] [Route("[controller]")] public class HomeController : Controller { [Route("Main")] // сопоставляется с Home/Main, либо с Store/Main [Route("Index")] // сопоставляется с Home/Index, либо с Store/Index public IActionResult Index() { return View(); } }