Кроме маршрутизации на основе условностей или Convention-Based Routing, которая рассматривалась в прошлых темах, фреймворк MVC позволяет использовать в приложении маршрутизацию на основе атрибутов. Такой тип маршрутизации еще называется Attribute-Based Routing. Атрибуты предоставляют более гибкий способ определения маршрутов.
Для определения маршрута непосредственно в контроллере, необходимо использовать атрибут [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".
Если в проекте планируется использовать только маршрутизацию на основе атрибутов, то в файле Program.cs мы можем не определять никаких маршрутов.
В этом случае для подключения в приложении возможностей маршрутизации достаточно применить метод MapControllers
без определения конкретных маршрутов:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllersWithViews(); var app = builder.Build(); app.MapControllers(); app.Run();
Но также мы можем комбинировать способы маршрутизации. При чем здесь надо учитывать, что маршрутизация на основе атрибутов имеет больший приоритет. Например, определим следующее действие контроллера:
public class HomeController : Controller { [Route("homepage")] public string Index() => "ASP.NET Core MVC on METANIT.COM"; }
В качестве параметра атрибут Route принимает шаблон URL, с которым будет сопоставляться запрошенный адрес. И даже если у нас определен стандартный маршрут в файле Program.cs:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllersWithViews(); var app = builder.Build(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
Запрос типа http://localhost:xxxx/home/index работать не будет, так как мы явно указали с помощью атрибута, что метод Index контроллера Home будет обрабатывать только запросы http://localhost:xxxx/homepage
Определение маршрутов с помощью атрибутов подчиняется тем же правилам, что определение маршрутов с помощью методе MapControllerRoute()
. Например, используем параметры
и ограничения:
public class HomeController : Controller { [Route("{name:minlength(3)}/{age:int}")] public string Person(string name, int age) { return $"name={name} | age={age}"; } }
И к такому методу мы сможем обратиться с запросом типа http://localhost:xxxx/tom/37.
Или другой пример - применение необязательного параметра:
public class HomeController : Controller { [Route("Home/Index/{id?}")] public string Test(int? id) { if(id is not null) return $"Параметр id={id}"; else return $"Параметр id неопределен"; } }
И к такому методу мы сможем обратиться с запросом типа http://localhost:xxxx/home/index или добавить параметр http://localhost:xxxx/home/index/5.
Допустим, у нас есть несколько методов, для которых определен свой маршрут, и мы хотим, чтобы эти маршруты начинались с одного определенного префикса. Например:
public class HomeController : Controller { [Route("main/index/{name}")] public string Index(string name) => name; [Route("main/{id:int}/{name:maxlength(10)}")] public string Test(int id, string name) => $" id={id} | name={name}"; }
Вместо того, чтобы добавлять префикс "main" к каждому маршруту, мы могли бы определить его глобально для всего контроллера:
[Route("main")] public class HomeController : Controller { [Route("index/{name}")] public string Index(string name) => name; [Route("{id:int}/{name:maxlength(10)}")] public string Test(int id, string name) => $" id={id} | name={name}"; }
От всех параметров шаблона маршрутов в атрибутах отличаются два параметра controller и action, которые ссылаются соответственно на контроллер и его действие и которые также можно получить в методе контроллера:
public class HomeController : Controller { [Route("{controller}/{action}")] public string Index() { var controller = RouteData.Values["controller"]; var action = RouteData.Values["action"]; return $"controller: {controller} | action: {action}"; } }
public class HomeController : Controller { [Route("{controller}/{action}")] public string Index(string controller, string action) { return $"controller: {controller} | action: {action}"; } }
С помощью атрибутов можно задать несколько маршрутов для одного метода. Например:
[Route("{controller}")] public class HomeController : Controller { [Route("")] // сопоставляется с Home [Route("Index")] // сопоставляется с Home/Index public string Index() => "Index Page"; }
Также для контроллера можно задать сразу несколько маршрутов:
[Route("Store")] [Route("{controller}")] public class HomeController : Controller { [Route("Main")] // сопоставляется с Home/Main, либо с Store/Main [Route("Index")] // сопоставляется с Home/Index, либо с Store/Index public string Index() => "Index Page"; }
Также параметры в атрибутах могут иметь значения по умолчанию:
public class HomeController : Controller { [Route("{controller=Home}/{action=Index}")] public string Index(string controller, string action) { return $"controller: {controller} | action: {action}"; } }
В данном случае методом Index будут сопоставляться такие запросы как http://localhost:xxxx/home/index, http://localhost:xxxx/home/ и http://localhost:xxxx/
Подобным образом можно задавать значения по умолчанию для других параметров, например:
public class HomeController : Controller { [Route("{name=Tom}")] public string Index(string name) => $"Name: {name}"; }
В данном случае если параметру name не передано значение, то он по умолчанию принимает в качестве значения строку "Tom"