قبل از معرفی فریم ورک mvc ، در asp.net کلاسیک یک رابطه مستقیم بین URL های درخواست شده از سوی کاربر و فایل های هارد دیسک سرور وجود داشت. سرور درخواست را از بروزر دریافت کرده و خروجی را از فایل مربوطه تحویل میداد. اما این روش در mvc کارآمد نیست چرا که درخواست ها از طریق اکشن متدها در کلاس های کنترلر پردازش میشوند. برای مدیریت URL ها فریم ورک MVC از یک Routing System بهره میبرد. در این مقاله چگونگی تنظیم و بکارگیری Routing System در MVC را با هم مرور کرده و خواهیم دید که چگونه این سیستم به شما اجازه ساخت URL های مورد نیازتان را میدهد. سپس ویژگی دیگری از MVC را به نام area معرفی میکنیم که با تقسیم یک پروژه MVC به بخش های کوچک تر ، مدیریت آن را ساده تر میکند.
مسیریابی (Routing) در ASP.NET MVC

دیدیم که در asp.net classic یک رابطه مستقیم بین URL های درخواست شده و فایل های سرور وجود دارد.به شکل زیر توجه کنید:

asp.net routing

در سیستم routing فریم ورک MVC ، دو کارکرد اصلی را میتوان در نظر گرفت:

اول بررسی URL های ورودی و تشخیص اینکه برای چه کنترلر و اکشن متدی این request در نظر گرفته شده است.

دوم تولید URL های خروجی. این URL ها در HTML نهایی ما که از view ها رندر میشود نشان داده خواهد شد. که با کلیک user بر روی این لینک ها یک متد خاصی فراخوانی خواهد شد (در این مرحله دوباره URL ورودی خواهیم داشت)

کار اصلی سیستم مسیریابی در mvc را میتوان اینطور در نظر گرفت که این سیستم URL های درخواست شده از سوی کاربر را به اکشن متد ها در کنترلرها map (نگاشت) میکند. در نگاه اول شاید اینطور بنظر برسد که ما مجبوریم برای هر درخواست URL ویژه آن را دستی وارد کنیم. اما در واقع routing system در خواست های رسیده را با یک URL Pattern مقایسه میکند. اگر pattern با URL رسیده مطابقت کند ، سیستم شروع به پردازش URL خواهد کرد. به URL زیر توجه کنید:

?
۱

http: //mysite.com/Admin/Index

ما میتوانیم URL ها را به قسمت های کوچکتری به نام segment تقسیم کنیم:

 

در نگاه ما مشخص است که سگمنت اول نام کنترلر و سگمنت دوم نام اکشن متد است. برای مشخص کردن همین موضوع برای سیستم مسیریابی ، باید یک URL Pattern به شکل زیر برای سیستم تعریف کنیم:

?
۱

{controller} / {action}

کار اصلی سیستم روتینگ نگاشت URL به pattern است و سپس جدا کردن مقادیر مورد نیاز متغیرهای segment از URL. متغیر های segment را با نام های controller و action در بین آکولاد در مثال بالا دیدید. بنابراین مقدار برای سگمنت اول Admin و برای سگمنت دوم Index خواهد بود.

بطور پیش فرض ، یک URL Pattern با هر URL ای که تعداد سگمنت درست داشته باشد match میشود. به عنوان مثال pattern ارائه شده در بالا با هر URL ای که دو سگمنت داشته باشد match خواهد شد.

two-segment-pattern

از آنجایی که routing system اطلاعاتی راجع به ساختار پروژه ما ندارد، pattern با URL هایی که دو سگمنت دارد match میشود حتی اگر کنترلر یا اکشن متدی با این نام نداشته باشیم. مانند مثال سوم
ساخت یک Route ساده در MVC

حال که یک pattern ساده در نظر گرفته ایم میتوانیم برای این pattern یک route تعریف کنیم. route ها در فایل RouteConfig.cs در پوشه App_Start از پروژه قرار دارند. محتوی ابتدایی این فایل به شکل زیر است:

?
۱
۲
۳
۴
۵
۶
۷
۸
۹
۱۰

public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute(“{resource}.axd/{*pathInfo}”);

routes.MapRoute(
name: “Default”,
url: “{controller}/{action}/{id}”,
defaults: new { controller = “Home”, action = “Index”, id = UrlParameter.Optional }
);
}

ویژوال استدیو دو route به متد RegisterRoutes اضافه میکند. اولی دستورالعملی برای سیستم روتینگ است تا از request ها برای فایل هایی با پسوند .axd صرف نظر کند. این فایل ها ، فایل های web resource شناخته میشوند.

ما برای register کردن route های خود از متد MapRoute استفاده میکنیم. این متد حداقل دو آرگومان میگیرد: یکی نام برای route و دیگری URL ای که route با آن هم خوانی دارد. مثل مثال ساده زیر:

?
۱
۲
۳
۴
۵

public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute(“{resource}.axd/{*pathInfo}”);

routes.MapRoute(“MyRoute”, “{controller}/{action}”);
}

این route با هر url دو قسمتی match خواهد شد. قسمت اول نام کنترلر و قسمت دوم نام اکشن متد را مشخص خواهد کرد. حال اگر ما یک URL به شکل /Admin/Index را درخواست کنیم، در واقع متد Index از کلاس AdminController را فراخوانی کرده ایم.

اما همانطور که دیدید ویژوال استدیو برخی مقادیر پیش فرض دیگری هم به متد MapRoute ارسال میکند:

?
۱
۲
۳
۴
۵
۶
۷
۸
۹
۱۰

public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute(“{resource}.axd/{*pathInfo}”);

routes.MapRoute(
name: “Default”,
url: “{controller}/{action}/{id}”,
defaults: new { controller = “Home”, action = “Index”, id = UrlParameter.Optional }
);
}

در default Route ، مقدار پیش فرض برای متغیر controller، مقدار Home و Index برای متغیر action است. متغیر id یک متغیر اختیاری است و این امر با انتساب مقدار UrlParameter.Optional به آن مشخص میشود. با داشتن یک متغیر اختیاری ، route ما با دامنه وسیع تری از URL ها match خواهد شد. اگر در URL یک سگمنت سومی هم باشد ، به متغیر id نسبت داده خواهد شد، در غیر این صورت از آن صرف نظر خواهد شد. متغیر

optional-segment

id برای مواقعی که کاربر مقداری را برای متغیر سگمنت میفرستد مناسب است. با داشتن این مقادیر پیش فرض ، route ما با URL هایی صفر یا یک یا دو یا سه سگمنت دارند ، match خواهد شد.
کار با Area در ASP.NET MVC

با بکاریری area ها در mvc میتوانیم پروژه خود را به بخش های کوچک تر با مدیریت راحت تر تقسیم کنیم. هرکدام از این بخش ها همانند یک پروژه MVC کوچک است و شامل پوشه هایی برای controller ها ، model ها , view ها میباشد. با این جداسازی کار کردن هم زمان چندین developer به روی پروژه هم ساده تر شده است.

فرض کنید میخواهیم یک area با نام admin به پروژه خود اضافه کنیم تا بطور اصولی بخش مدیریت سایت را از بخش userinteface کاربر جدا کنیم.

تفاوتی که این area با فرمت کلی پروژه MVC ما دارد ، وجود یک فایل به اسم AdminAreaRegistration.cs است.

?
۱
۲
۳
۴
۵
۶
۷
۸
۹
۱۰
۱۱
۱۲
۱۳
۱۴
۱۵
۱۶
۱۷

namespace EventRegistration.Areas.Admin {
public class AdminAreaRegistration : AreaRegistration {

public override string AreaName {
get {
return “Admin”;
}
}
public override void RegisterArea(AreaRegistrationContext context) {
context.MapRoute(
“Admin_default”,
“Admin/{controller}/{action}/{id}”,
new { action = “Index”, id = UrlParameter.Optional }
);
}
}
}

همانطور که میبینید این کلاس با متد RegisterArea یک route با URL Pattern بصورت Admin/{controller}/{action}/{id} میسازد .

لازم نیست ما نگران register این area باشیم چون خود mvc این کارکرد را بطور اتوماتیک در متد زیر دنبال میکند:

?
۱
۲
۳
۴
۵
۶

protected void Application_Start() {

AreaRegistration.RegisterAllAreas();
….

}

این متد در فایل Global.asax قرار دارد.

تمامی مواردی را که در پروژه قابل اجرا بود در area هم میتوانید پیاده کنید. ساخت کنترلر ها ، view ها و تعریف route ها…
حل مشکل هم نامی کنترلرها

فرض کنید یک کنترل با نام Registration هم در area خود ساخته ایم و هم در قسمت main پروژه خود. اگر به آدرس /Admin/Registration/Index برویم ، بدون مشکل صفحه را مشاهده خواهیم کرد. اما با رفتن به مسیر Registration/Index با ارور Multiple types were found مواجه خواهیم شد. این به این دلیل است که زمانی که یک URL با یک route در area همخوانی دارد، mvc تنها کنترلرهای همان ناحیه را در نظر میگیرد، اما اگر یک URL با route در بخش اصلی پروژه همخوانی داشته باشد، mvc تمامی کنترلرها را کاندید میکند و تفاوتی یرای کنترلر های بخش area و بخش اصلی پروژه قائل نخواهد بود و با error مواجه خواهیم شد.

برای حل این مشکل باید به mvc اعلام کنیم که زمانی که یک URL با route ای در بخش اصلی پروژه همخوانی دارد، ابتدا باید کدام namespace را در نظر بگیرد. به این منظور متد RegisterRoutes در فایل Global.asax را ویرایش میکنیم:

?
۱
۲
۳
۴
۵
۶
۷
۸
۹

public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute(“{resource}.axd/{*pathInfo}”);
routes.MapRoute(
“Default”,
“{controller}/{action}/{id}”,
new { controller = “Registration”, action = “Index”, id = UrlParameter.Optional },
new string[] {“UI.Web.Controllers”}
);
}

برای این کار یک آرگومان رشته ای جدید شامل نام namespace با اولویت بیشتر را مشخص میکنیم تا mvc به درستی controller را انتخاب کند.

نکته بعدی در area ها ساخت لینک هاست. در مواردی که میخواهیم به اکشن متد های داخل یک area لینک بدهیم ، مشکلی وجود ندارد و تغییری لازم نیست اعمال شود. اما در مواردی که میخواهیم به یک اکشن لینک در area دیگری لینک بدهیم باید متغیر area را تعریف کرده و نام area مقصد را به آن نسبت دهیم:

?
۱

@Html.ActionLink(“Click me to go to another area”, “Index”, “Home”, new { area = “Admin” })

که خروجی زیر را خواهیم داشت:

?
۱

< a href=”/Admin/Home/Index”>Click me to go to another area</ a >

و برای لینک دادن به اکشنی در قسمت اصلی پروژه باید نام area را خالی بگذاریم:

?
۱

@Html.ActionLink(“Click me to go to another area”, “Index”, “Home”, new { area = “” })