Маршрутизация ASP.NET MVC через атрибуты метода
В StackOverflow Podcast # 54, Джефф упоминает, что они регистрируют свои URL-маршруты в базе данных StackOverflow через атрибут выше метода, который обрабатывает маршрут. Похоже на хорошую концепцию (с предостережением, которое Фил Хаак рассказал о приоритетах маршрутов).
Может ли кто-нибудь предоставить образец, чтобы это произошло?
Кроме того, какие-либо "лучшие практики" для использования этого стиля маршрутизации?
Ответы
Ответ 1
UPDATE. Это было опубликовано на codeplex. Полный исходный код, а также предварительно скомпилированная сборка доступны для загрузки. Я еще не успел опубликовать документацию на сайте, так что это сообщение SO должно быть достаточно.
UPDATE. Я добавил несколько новых атрибутов для обработки 1) упорядочения маршрута, 2) ограничений параметров маршрута и 3) значений параметров маршрута маршрута. В приведенном ниже тексте указано это обновление.
Я действительно сделал что-то подобное для своих проектов MVC (я не знаю, как Jeff делает это с помощью stackoverflow). Я определил набор пользовательских атрибутов: UrlRoute, UrlRouteParameterConstraint, UrlRouteParameterDefault. Они могут быть привязаны к методам действий контроллера MVC, чтобы автоматически связывать маршруты, ограничения и значения по умолчанию.
Использование примера:
(Обратите внимание, что этот пример несколько надуман, но демонстрирует эту функцию)
public class UsersController : Controller
{
// Simple path.
// Note you can have multiple UrlRoute attributes affixed to same method.
[UrlRoute(Path = "users")]
public ActionResult Index()
{
return View();
}
// Path with parameter plus constraint on parameter.
// You can have multiple constraints.
[UrlRoute(Path = "users/{userId}")]
[UrlRouteParameterConstraint(Name = "userId", Regex = @"\d+")]
public ActionResult UserProfile(int userId)
{
// ...code omitted
return View();
}
// Path with Order specified, to ensure it is added before the previous
// route. Without this, the "users/admin" URL may match the previous
// route before this route is even evaluated.
[UrlRoute(Path = "users/admin", Order = -10)]
public ActionResult AdminProfile()
{
// ...code omitted
return View();
}
// Path with multiple parameters and default value for the last
// parameter if its not specified.
[UrlRoute(Path = "users/{userId}/posts/{dateRange}")]
[UrlRouteParameterConstraint(Name = "userId", Regex = @"\d+")]
[UrlRouteParameterDefault(Name = "dateRange", Value = "all")]
public ActionResult UserPostsByTag(int userId, string dateRange)
{
// ...code omitted
return View();
}
Определение атрибута UrlRouteAttribute:
/// <summary>
/// Assigns a URL route to an MVC Controller class method.
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class UrlRouteAttribute : Attribute
{
/// <summary>
/// Optional name of the route. If not specified, the route name will
/// be set to [controller name].[action name].
/// </summary>
public string Name { get; set; }
/// <summary>
/// Path of the URL route. This is relative to the root of the web site.
/// Do not append a "/" prefix. Specify empty string for the root page.
/// </summary>
public string Path { get; set; }
/// <summary>
/// Optional order in which to add the route (default is 0). Routes
/// with lower order values will be added before those with higher.
/// Routes that have the same order value will be added in undefined
/// order with respect to each other.
/// </summary>
public int Order { get; set; }
}
Определение UrlRouteParameterConstraintAttribute:
/// <summary>
/// Assigns a constraint to a route parameter in a UrlRouteAttribute.
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class UrlRouteParameterConstraintAttribute : Attribute
{
/// <summary>
/// Name of the route parameter on which to apply the constraint.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Regular expression constraint to test on the route parameter value
/// in the URL.
/// </summary>
public string Regex { get; set; }
}
Определение UrlRouteParameterDefaultAttribute:
/// <summary>
/// Assigns a default value to a route parameter in a UrlRouteAttribute
/// if not specified in the URL.
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class UrlRouteParameterDefaultAttribute : Attribute
{
/// <summary>
/// Name of the route parameter for which to supply the default value.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Default value to set on the route parameter if not specified in the URL.
/// </summary>
public object Value { get; set; }
}
Изменения в Global.asax.cs:
Заменить вызовы MapRoute одним вызовом функции RouteUtility.RegisterUrlRoutesFromAttributes:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
RouteUtility.RegisterUrlRoutesFromAttributes(routes);
}
Определение RouteUtility.RegisterUrlRoutesFromAttributes:
Полный источник находится на codeplex. Перейдите на сайт, если у вас есть отзывы или отчеты об ошибках.
Ответ 2
Вы также можете попробовать AttributeRouting, который доступен из github или через nuget.
Это бесстыдный плагин, так как я автор проекта. Но, если я не очень доволен этим. Возможно, вы тоже. В репозитории github имеется много документации и пример кода wiki.
С помощью этой библиотеки вы можете сделать много:
- Украсьте свои действия с помощью атрибутов GET, POST, PUT и DELETE.
- Сопоставьте несколько маршрутов с одним действием, упорядочив их с помощью свойства Order.
- Укажите значения по умолчанию и ограничения маршрута, используя атрибуты.
- Укажите необязательные параметры с помощью простого? перед именем параметра.
- Укажите имя маршрута для поддержки именованных маршрутов.
- Определить области MVC на контроллере или базовом контроллере.
- Группируйте или объединяйте маршруты вместе с использованием префиксов маршрута, применяемых к контроллеру или базовому контроллеру.
- Поддержка устаревших URL-адресов.
- Установите приоритет маршрутов между маршрутами, определенными для действия, внутри контроллера, а также между контроллерами и базовыми контроллерами.
- Сгенерировать строчные исходящие URL в нижнем регистре.
- Определите свои собственные пользовательские правила маршрута и примените их к контроллеру для генерации маршрутов действий в контроллере без атрибутов шаблонов (подумайте о стиле RESTful).
- Отлаживайте свои маршруты, используя поставляемый HttpHandler.
Я уверен, что есть другие вещи, которые я забываю. Проверьте это. Безболезненно установить через nuget.
ПРИМЕЧАНИЕ. Начиная с 4/16/12, AttributeRouting также поддерживает новую инфраструктуру веб-API. На всякий случай вы ищете что-то, что может с этим справиться. Спасибо subkamran!
Ответ 3
1. Загрузите RiaLibrary.Web.dll и укажите его в своем веб-проекте ASP.NET MVC
2. Разверните методы контроллера с помощью атрибутов [Url]:
public SiteController : Controller
{
[Url("")]
public ActionResult Home()
{
return View();
}
[Url("about")]
public ActionResult AboutUs()
{
return View();
}
[Url("store/{?category}")]
public ActionResult Products(string category = null)
{
return View();
}
}
BTW, '?' параметр "{? category}" означает, что он является необязательным. Вам не нужно будет указывать это явно в значениях по умолчанию маршрута, что равно этому:
routes.MapRoute("Store", "store/{category}",
new { controller = "Store", action = "Home", category = UrlParameter.Optional });
3. Обновить файл Global.asax.cs
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoutes(); // This does the trick
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
}
}
Как установить значения по умолчанию и ограничения? Пример:
public SiteController : Controller
{
[Url("admin/articles/edit/{id}", Constraints = @"id=\d+")]
public ActionResult ArticlesEdit(int id)
{
return View();
}
[Url("articles/{category}/{date}_{title}", Constraints =
"date=(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])")]
public ActionResult Article(string category, DateTime date, string title)
{
return View();
}
}
Как настроить порядок? Пример:
[Url("forums/{?category}", Order = 2)]
public ActionResult Threads(string category)
{
return View();
}
[Url("forums/new", Order = 1)]
public ActionResult NewThread()
{
return View();
}
Ответ 4
Это сообщение просто для расширения ответа DSO.
При преобразовании моих маршрутов в атрибуты мне нужно было обработать атрибут ActionName. Итак, в GetRouteParamsFromAttribute:
ActionNameAttribute anAttr = methodInfo.GetCustomAttributes(typeof(ActionNameAttribute), false)
.Cast<ActionNameAttribute>()
.SingleOrDefault();
// Add to list of routes.
routeParams.Add(new MapRouteParams()
{
RouteName = routeAttrib.Name,
Path = routeAttrib.Path,
ControllerName = controllerName,
ActionName = (anAttr != null ? anAttr.Name : methodInfo.Name),
Order = routeAttrib.Order,
Constraints = GetConstraints(methodInfo),
Defaults = GetDefaults(methodInfo),
});
Также я нашел, что обозначение маршрута не подходит. Имя динамически создается с именем controllerName.RouteName. Но мои имена маршрутов являются константными строками в классе контроллера, и я использую эти константы для вызова Url.RouteUrl. Поэтому мне действительно нужно, чтобы имя маршрута в атрибуте являлось фактическим именем маршрута.
Еще одна вещь, которую я буду делать, - это преобразовать атрибуты по умолчанию и ограничения в AttributeTargets.Parameter, чтобы я мог привязать их к параметрам.
Ответ 5
Я объединил эти два подхода в версию Франкенштейна для тех, кто этого хочет. (Мне понравилась дополнительная нотация параметра, но также подумал, что они должны быть отдельными атрибутами по умолчанию/ограничениям, а не всем, смешанным в один).
http://github.com/djMax/AlienForce/tree/master/Utilities/Web/
Ответ 6
Мне нужно было настроить маршрутизацию ITCloud в asp.net mvc 2 с помощью AsyncController - для этого просто отредактируйте класс RouteUtility.cs в источнике и перекомпилируйте. Вы должны отключить "Завершено" от имени действия в строке 98
// Add to list of routes.
routeParams.Add(new MapRouteParams()
{
RouteName = String.IsNullOrEmpty(routeAttrib.Name) ? null : routeAttrib.Name,
Path = routeAttrib.Path,
ControllerName = controllerName,
ActionName = methodInfo.Name.Replace("Completed", ""),
Order = routeAttrib.Order,
Constraints = GetConstraints(methodInfo),
Defaults = GetDefaults(methodInfo),
ControllerNamespace = controllerClass.Namespace,
});
Затем, в AsyncController, украсьте XXXXCompleted ActionResult знакомыми атрибутами UrlRoute
и UrlRouteParameterDefault
:
[UrlRoute(Path = "ActionName/{title}")]
[UrlRouteParameterDefault(Name = "title", Value = "latest-post")]
public ActionResult ActionNameCompleted(string title)
{
...
}
Надеюсь, что это поможет кому-то с той же проблемой.