Routing مکانیزم مسیریابی ASP.NET MVC است، که یک URI را به یک اکشن متد نگاشت میکند. MVC 5 نوع جدیدی از مسیر یابی را پشتیبانی میکند که Attribute Routing یا مسیریابی نشانه ای نام دارد. همانطور که از نامش پیداست، مسیریابی نشانه ای از Attributeها برای این امر استفاده میکند. این روش به شما کنترل بیشتری روی URIهای اپلیکیشن تان میدهد.
مشخص کردن اختیاری بودن پارامتر ها، باید در آخر لیست constraints تعریف شود:
قطعه کد زیر نحوه رجیستر کردن این constraint را نشان میدهد:
حالا میتوانید این محدودیت سفارشی را روی مسیرها اعمال کنید:
میتوانید لینکی با استفاده از Url.RouteUrl تولید کنید:
با این کنترلر، فراخوانی تولید لینک زیر، رشته "Admin/menu/show-options/" را بدست میدهد:
به منظور تعریف یک پیشوند سفارشی برای یک ناحیه، که با نام خود ناحیه مورد نظر متفاوت است میتوانید از پارامتر AreaPrefix استفاده کنید. بعنوان مثال:
اگر از ناحیهها هم بصورت مسیریابی نشانه ای، و هم بصورت متداول (که با کلاسهای AreaRegistration پیکربندی میشوند) استفاده میکنید باید مطمئن شوید که رجیستر کردن نواحی اپلیکیشن پس از مسیریابی نشانه ای پیکربندی میشود. به هر حال رجیستر کردن ناحیهها پیش از تنظیم مسیرها بصورت متداول باید صورت گیرد. دلیل آن هم مشخص است، برای اینکه درخواستهای ورودی بدرستی با مسیرهای تعریف شده تطبیق داده شوند، باید ابتدا attribute routes، سپس area registration و در آخر default route رجیستر شوند. بعنوان مثال:
مدل قبلی مسیریابی (conventional-routing) هنوز کاملا پشتیبانی میشود. در واقع میتوانید هر دو تکنیک را بعنوان مکمل یکدیگر در یک پروژه استفاده کنید.
هنگامی که قوانین مسیریابی در کنار اکشن متدها تعریف میشوند، یعنی در یک فایل سورس و نه در یک کلاس پیکربندی خارجی، درک و فهم نگاشت URIها به اکشنها واضحتر و راحت میشود. تعریف مسیر قبلی، میتواند توسط یک attribute ساده بدین صورت نگاشت شود:
همچنین میتوانید مدل قبلی مسیریابی را با تکنیک جدید تلفیق کنید.
در این مثال، هر دو مسیر books/ و books/1430210079/ به اکشن متد "View" نگاشت میشوند، مسیر اول تمام کتابها را لیست میکند، و مسیر دوم جزئیات کتابی مشخص را لیست میکند. هر دو مسیر books/lang/ و books/lang/en/ به یک شکل نگاشت میشوند، چرا که مقدار پیش فرض این پارامتر en تعریف شده.
همچنین میتوانید با استفاده از خاصیت [RoutePrefix] یک پیشوند عمومی برای کل کنترلر تعریف کنید:
در صورت لزوم، میتوانید برای بازنویسی (override) پیشوند مسیرها از کاراکتر ~ استفاده کنید:
در اینجا، مسیر اول تنها در صورتی انتخاب میشود که قسمت id در URI یک مقدار integer باشد. در غیر اینصورت مسیر دوم انتخاب خواهد شد.
در این پست قابلیتها و گزینههای اساسی مسیریابی نشانه ای را بررسی میکنیم.
- چرا مسیریابی نشانه ای؟
- فعال سازی مسیریابی نشانه ای
- پارامترهای اختیاری URI و مقادیر پیش فرض
- پیشوند مسیر ها
- مسیر پیش فرض
- محدودیتهای مسیر ها
- محدودیتهای سفارشی
- نام مسیر ها
- ناحیهها (Areas)
چرا مسیریابی نشانه ای
برای مثال یک وب سایت تجارت آنلاین بهینه شده اجتماعی، میتواند مسیرهایی مانند لیست زیر داشته باشد:
- {productId:int}/{productTitle}
- {username}
- {username}/catalogs/{catalogId:int}/{catalogTitle}
نگاشت میشود به: (CatalogsController.Show(string username, int catalogId
در نسخه قبلی ASP.NET MVC، قوانین مسیریابی در فایل RouteConfig.cs تعریف میشدند، و اشاره به اکشنهای کنترلرها به نحو زیر انجام میشد:
routes.MapRoute( name: "ProductPage", url: "{productId}/{productTitle}", defaults: new { controller = "Products", action = "Show" }, constraints: new { productId = "\\d+" } );
[Route("{productId:int}/{productTitle}")] public ActionResult Show(int productId) { ... }
فعال سازی Attribute Routing
برای فعال سازی مسیریابی نشانه ای، متد MapMvcAttributeRoutes را هنگام پیکربندی فراخوانی کنید.
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapMvcAttributeRoutes(); } }
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapMvcAttributeRoutes(); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); }
پارامترهای اختیاری URI و مقادیر پیش فرض
می توانید با اضافه کردن یک علامت سوال به پارامترهای مسیریابی، آنها را optional یا اختیاری کنید. برای تعیین مقدار پیش فرض هم از فرمت parameter=value استفاده میکنید.
public class BooksController : Controller { // eg: /books // eg: /books/1430210079 [Route("books/{isbn?}")] public ActionResult View(string isbn) { if (!String.IsNullOrEmpty(isbn)) { return View("OneBook", GetBook(isbn)); } return View("AllBooks", GetBooks()); } // eg: /books/lang // eg: /books/lang/en // eg: /books/lang/he [Route("books/lang/{lang=en}")] public ActionResult ViewByLanguage(string lang) { return View("OneBook", GetBooksByLanguage(lang)); } }
پیشوند مسیرها (Route Prefixes)
برخی اوقات، تمام مسیرها در یک کنترلر با یک پیشوند شروع میشوند. بعنوان مثال:
public class ReviewsController : Controller { // eg: /reviews [Route("reviews")] public ActionResult Index() { ... } // eg: /reviews/5 [Route("reviews/{reviewId}")] public ActionResult Show(int reviewId) { ... } // eg: /reviews/5/edit [Route("reviews/{reviewId}/edit")] public ActionResult Edit(int reviewId) { ... } }
[RoutePrefix("reviews")] public class ReviewsController : Controller { // eg.: /reviews [Route] public ActionResult Index() { ... } // eg.: /reviews/5 [Route("{reviewId}")] public ActionResult Show(int reviewId) { ... } // eg.: /reviews/5/edit [Route("{reviewId}/edit")] public ActionResult Edit(int reviewId) { ... } }
[RoutePrefix("reviews")] public class ReviewsController : Controller { // eg.: /spotlight-review [Route("~/spotlight-review")] public ActionResult ShowSpotlight() { ... } ... }
مسیر پیش فرض
می توانید خاصیت [Route] را روی کنترلر اعمال کنید، تا اکشن متد را بعنوان یک پارامتر بگیرید. این مسیر سپس روی تمام اکشن متدهای این کنترلر اعمال میشود، مگر آنکه یک [Route] بخصوص روی اکشنها تعریف شده باشد.
[RoutePrefix("promotions")] [Route("{action=index}")] public class ReviewsController : Controller { // eg.: /promotions public ActionResult Index() { ... } // eg.: /promotions/archive public ActionResult Archive() { ... } // eg.: /promotions/new public ActionResult New() { ... } // eg.: /promotions/edit/5 [Route("edit/{promoId:int}")] public ActionResult Edit(int promoId) { ... } }
محدودیتهای مسیر ها
با استفاده از Route Constraints میتوانید نحوه جفت شدن پارامترها در قالب مسیریابی را محدود و کنترل کنید. فرمت کلی {parameter:constraint} است. بعنوان مثال:
// eg: /users/5 [Route("users/{id:int}"] public ActionResult GetUserById(int id) { ... } // eg: users/ken [Route("users/{name}"] public ActionResult GetUserByName(string name) { ... }
جدول زیر constraintها یا محدودیت هایی که پشتیبانی میشوند را لیست میکند.
مثال | توضیحات | محدودیت |
{x:alpha} | کاراکترهای الفبای لاتین را تطبیق (match) میدهد (a-z, A-Z). | alpha |
{x:bool} | یک مقدار منطقی را تطبیق میدهد. | bool |
{x:datetime} | یک مقدار DateTime را تطبیق میدهد. | datetime |
{x:decimal} | یک مقدار پولی را تطبیق میدهد. | decimal |
{x:double} | یک مقدار اعشاری 64 بیتی را تطبیق میدهد. | double |
{x:float} | یک مقدار اعشاری 32 بیتی را تطبیق میدهد. | float |
{x:guid} | یک مقدار GUID را تطبیق میدهد. | guid |
{x:int} | یک مقدار 32 بیتی integer را تطبیق میدهد. | int |
{(x:length(6} {(x:length(1,20} | رشته ای با طول تعیین شده را تطبیق میدهد. | length |
{x:long} | یک مقدار 64 بیتی integer را تطبیق میدهد. | long |
{(x:max(10} | یک مقدار integer با حداکثر مجاز را تطبیق میدهد. | max |
{(x:maxlength(10} | رشته ای با حداکثر طول تعیین شده را تطبیق میدهد. | maxlength |
{(x:min(10} | مقداری integer با حداقل مقدار تعیین شده را تطبیق میدهد. | min |
{(x:minlength(10} | رشته ای با حداقل طول تعیین شده را تطبیق میدهد. | minlength |
{(x:range(10,50} | مقداری integer در بازه تعریف شده را تطبیق میدهد. | range |
{(${x:regex(^\d{3}-\d{3}-\d{4} | یک عبارت با قاعده را تطبیق میدهد. | regex |
توجه کنید که بعضی از constraint ها، مانند "min" آرگومانها را در پرانتز دریافت میکنند.
می توانید محدودیتهای متعددی روی یک پارامتر تعریف کنید، که باید با دونقطه جدا شوند. بعنوان مثال:
// eg: /users/5 // but not /users/10000000000 because it is larger than int.MaxValue, // and not /users/0 because of the min(1) constraint. [Route("users/{id:int:min(1)}")] public ActionResult GetUserById(int id) { ... }
// eg: /greetings/bye // and /greetings because of the Optional modifier, // but not /greetings/see-you-tomorrow because of the maxlength(3) constraint. [Route("greetings/{message:maxlength(3)?}")] public ActionResult Greet(string message) { ... }
محدودیتهای سفارشی
با پیاده سازی قرارداد IRouteConstraint میتوانید محدودیتهای سفارشی بسازید. بعنوان مثال، constraint زیر یک پارامتر را به لیستی از مقادیر قابل قبول محدود میکند:
public class ValuesConstraint : IRouteConstraint { private readonly string[] validOptions; public ValuesConstraint(string options) { validOptions = options.Split('|'); } public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) { object value; if (values.TryGetValue(parameterName, out value) && value != null) { return validOptions.Contains(value.ToString(), StringComparer.OrdinalIgnoreCase); } return false; } }
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); var constraintsResolver = new DefaultInlineConstraintResolver(); constraintsResolver.ConstraintMap.Add("values", typeof(ValuesConstraint)); routes.MapMvcAttributeRoutes(constraintsResolver); } }
public class TemperatureController : Controller { // eg: temp/celsius and /temp/fahrenheit but not /temp/kelvin [Route("temp/{scale:values(celsius|fahrenheit)}")] public ActionResult Show(string scale) { return Content("scale is " + scale); } }
نام مسیر ها
می توانید به مسیرها یک نام اختصاص دهید، با این کار تولید URIها هم راحتتر میشوند. بعنوان مثال برای مسیر زیر:
[Route("menu", Name = "mainmenu")] public ActionResult MainMenu() { ... }
<a href="@Url.RouteUrl("mainmenu")">Main menu</a>
ناحیهها (Areas)
برای مشخص کردن ناحیه ای که کنترلر به آن تعلق دارد میتوانید از خاصیت [RouteArea] استفاده کنید. هنگام استفاده از این خاصیت، میتوانید با خیال راحت کلاس AreaRegistration را از ناحیه مورد نظر حذف کنید.
[RouteArea("Admin")] [RoutePrefix("menu")] [Route("{action}")] public class MenuController : Controller { // eg: /admin/menu/login public ActionResult Login() { ... } // eg: /admin/menu/show-options [Route("show-options")] public ActionResult Options() { ... } // eg: /stats [Route("~/stats")] public ActionResult Stats() { ... } }
Url.Action("Options", "Menu", new { Area = "Admin" })
[RouteArea("BackOffice", AreaPrefix = "back-office")]
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapMvcAttributeRoutes(); AreaRegistration.RegisterAllAreas(); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); }