Одним из нововведений в ASP.NET MVC 5 является задание маршрутов при помощи атрибутов, по аналогии с Web API. Такой подход позволяет указывать необходимые настройки непосредственно в самих контроллерах.
Подключаем использование атрибутов
Чтобы активировать маршруты, заданные атрибутами, необходимо вызвать метод MapMvcAttributeRoutes() класса RouteCollection:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(...);
}
}
При этом необходимо учесть что:
- При использовании и атрибутов и правил для определения маршрутов, вызовы MapRoute() должны находиться после MapMvcAttributeRoutes().
- У атрибутов приоритет выше, чем у правил:
- действия с атрибутами получают получают маршруты согласно значениям этих атрибутов не смотря на заданные правила;
- действия без атрибутов получают маршруты согласно правилам.
- При использовании только атрибутов (без правил), не отмеченные ими действия будут не доступны.
Указание на уровне контроллера
Для контроллера можно задать следующие атрибуты:
- [RouteArea("<значение>")] – устанавливает область для контроллера.
- [RoutePrefix("<значение>")] – определяет значения префикса в маршруте. По сути, это позволяет переопределить имя контроллера.
- [Route("{action=<значение>}")] – задает значения по умолчанию для всех действий (в данном примере это action).
Указанные значения будут добавляться к определениям маршрутов для всем действий этого контроллера.
Указание на уровне действия
Для отдельного действия применяется атрибут [Route]. Рассмотрим его использование на конкретных примерах.
Определение маршрута
public class BooksController : Controller
{
// Маршрут: /Books/Catalog/
[Route("Books/Catalog/")]
public async Task<ActionResult> Catalog() { ... }
}
[RouteArea("Admin")]
[RoutePrefix("Profiles")]
public class UsersController : Controller
{
// Маршрут: /Admin/Profiles/List/
[Route("List")]
public async Task<ActionResult> List() { ... }
// Маршрут: /Admin/Profiles/{id}/Edit
[Route("{id}/Edit")]
public async Task<ActionResult> Edit(int id) { ... }
}
В последующих примерах будем считать что у контроллера не заданы [RouteArea] и [RoutePrefix].
Значения по умолчанию
[Route("Books/Read/{id=1}")]
public async Task<ActionResult> Read(int id) { ... }
Опциональные параметры ("?")
[Route("Books/View/{id?}")]
public async Task<ActionResult> View(int? id) { ... }
Действие View будет соответствовать как маршруту /Books/View так и /Books/View/{id}.
Указание абсолютного маршрута ("~")
// Маршрут: /dashboard
[Route("~/dashboard")]
public async Task<ActionResult> UserDashboard() { ... }
Ограничение типа значений параметров
Возможны следующие ограничения значений параметров:
Ограничение |
Описание |
Пример |
alpha |
Буквы латинского алфавита: A-Z и a-z. |
{val:alpha} |
bool |
Булевское значение. |
{val:bool} |
datetime |
Тип DateTime. |
{val:datetime} |
decimal |
Десятичное значение. |
{val:decimal} |
double |
Тип double (64-битное число с плавающей точкой). |
{val:double} |
float |
Тип float (32-битное число с плавающей точкой). |
{val:float} |
guid |
Тип Guid. |
{val:guid} |
int |
32-битное целое число. |
{val:int} |
length |
Позволяет указать точную длину строки или ее диапазон. |
{val:length(8)}
{val:length(3,6)} |
long |
64-битное целое число. |
{val:long} |
max |
Максимальное значение параметра. |
{val:max(16)} |
maxlength |
Максимальная длина строки. |
{val:maxlength(8)} |
min |
Минимальное значение параметра. |
{val:min(10)} |
minlength |
Минимальная длина строки. |
{val:minlength(6)} |
range |
Устанавливает диапазон. |
{val:range(10,20)} |
regex |
Задает регулярное выражение, которому должно соответствовать значение. |
{val:regex(^\d{3}-\d{3}-\d{4}$)} |
Ограничитель отделяется от имени параметра двоеточием, Например, вот указание что id должен быть только целым числом:
[Route("View/{id:int}")]
public async Task<ActionResult> View(int id) { ... }
Также можно указать несколько ограничений (в примере – целое числа не меньше 1):
[Route("View/{id:int:min(1)}")]
public async Task<ActionResult> View(int id) { ... }
Если параметр является опциональным, то символ "?" ставится после ограничителей:
[Route("View/{id:int:min(1)?}")]
public async Task<ActionResult> View(int? id) { ... }
Собственные ограничение типа значений параметров
Разработчик может создать собственные ограничения. Для этого необходимо реализовать интерфейс IRouteConstraint.
public class MyConstraint : IRouteConstraint
{
private readonly int _param;
public MyConstraint(int param)
{
this._param = param;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
...
}
}
Метод Match() должен вернуть true, если значение удовлетворяет необходимым условиям.
Остается зарегистрировать созданное ограничение и его имя в методе RegisterRoutes().
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
var constraintsResolver = new DefaultInlineConstraintResolver();
constraintsResolver.ConstraintMap.Add("myConstraint", typeof(MyConstraint));
routes.MapMvcAttributeRoutes(constraintsResolver);
}
}
После этого можно использовать его в атрибутах маршрутизации:
[Route("View/{id:myConstraint(42)")]
public async Task<ActionResult> View(int id) { ... }
Имена маршрутов
Атрибуты позволяют определять имена для маршрутов с помощью параметра Name. Такой подход может быть полезен для упрощения поддержки проекта. Например, установим имя HomePage для главной страницы сайта:
public class HomeController : Controller
{
[Route("", Name = "HomePage")]
public async Task<ActionResult> Index() { ... }
}
Теперь все ссылки на нее можно задавать следующим образом:
<a href="@Url.RouteUrl("HomePage")">Home</a>
А теперь сделаем так, что бы главной страницей был список книг. Все что надо сделать – это переместить имя на нужное действие:
public class BooksController : Controller
{
[Route("Books/Catalog/", Name = "HomePage")]
public async Task<ActionResult> Catalog() { ... }
}
Ссылки на страницах обновятся автоматически.