MVC路由解析---MapRoute


文章引導

              MVC路由解析---IgnoreRoute 

              MVC路由解析---MapRoute 

              MVC路由解析---UrlRoutingModule  

              Area的使用

引言

               前面我們講了IgnoreRoute鏈接

               現在我們講講核心的MapRoute,還是提前准備Reflection工具,若是沒准備,可以看“”MVC路由深入詳解1---IgnoreRoute”中的System.Web.dll源碼

一.RouteCollection

               我們來看看RouteCollection.MapRoute,截圖如下:

                        

               相信大家看到了RouteCollectionExtensions是一個靜態類,是對RouteCollection的擴展(關於擴展方法的大家可以百度,此處不做詳細描述)。好家伙,我們看看這個擴展方法走向何方(這個時候就是Reflection發揮作用的時候了)。

               引用“”MVC路由深入詳解1---IgnoreRoute”中的內容,看看MapRoute的參數傳入的是什么:

                              name:       "Default"

                              url:           "{controller}/{action}/{id}"

                              defaults:   new { controller = "Home", action = "Index", id = UrlParameter.Optional }  --->這是個匿名類型

 

               我們看看擴展方法去了哪里:

                              public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults) => routes.MapRoute(name, url, defaults, null); 

               我們接着往下走       

                              public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints) =>routes.MapRoute(name, url, defaults, constraints, null);

               接着

        public Route MapRoute(string name,string url,object defaults,object constraints=null,string[] param=null)
        {
            if(url==null)
            {
                throw new ArgumentNullException("url");
            }
            Route route = new Route(url, new MvcRouteHandler())
            {
                Defaults=CreateRouteValueDictionaryUncached(defaults),
                Constraints=CreateRouteValueDictionaryUncached(constraints),
                DataTokens=new System.Web.Routing.RouteValueDictionary()
            };
            ConstraintValidation.Validate(route);
            if ((param != null) && (param.Length > 0))
            {
                route.DataTokens["Namespaces"] = param;
            }
            Add(name, route);
            return route;
        }

                上面新建了一個Route,Route就是一條具體的路由 ,new Route(url, new MvcRouteHandler())傳入規則url和new MvcRouteHandler()。

二.CreateRouteValueDictionaryUncached      

        private static System.Web.Routing.RouteValueDictionary CreateRouteValueDictionaryUncached(object values)
        {
            IDictionary<string, object> dictionary = values as IDictionary<string, object>;
            if (dictionary != null)
            {
                return new System.Web.Routing.RouteValueDictionary(dictionary);
            }
            return System.Web.WebPages.TypeHelper.ObjectToDictionaryUncached(values);
        }          

三.MvcRouteHandler

               我們繼續拆解MvcRouteHandler

public class MvcRouteHandler: System.Web.Routing.IRouteHandler
    {
        System.Web.Mvc.IControllerFactory _controllerFactory;
        public MvcRouteHandler() { } public MvcRouteHandler(System.Web.Mvc.IControllerFactory controllFactory) { _controllerFactory = controllFactory; } protected virtual IHttpHandler GetHttpHandler(System.Web.Routing.RequestContext requestContext) { //SetSessionStateBehavior:在派生類重寫時,設置支持HTTP請求所必須的會話狀態行為的類型  requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext)); return new MvcHandler(requestContext); } IHttpHandler System.Web.Routing.IRouteHandler.GetHttpHandler(System.Web.Routing.RequestContext requestContext) { return GetHttpHandler(requestContext); } protected virtual System.Web.SessionState.SessionStateBehavior GetSessionStateBehavior(System.Web.Routing.RequestContext requestContext) { string str = (string)requestContext.RouteData.Values["controller"]; if (string.IsNullOrEmpty(str)) { throw new InvalidOperationException(System.Web.Mvc.Properties.MvcResources.MvcRouteHandler_RouteValuesHasNoController); } IControllerFactory factory = _controllerFactory ?? ControllerBuilder.Current.GetControllerFactory(); return factory.GetControllerSessionBehavior(requestContext, str); } }

四.RouteValueDictionary

             RouteValueDictionary是對Dictionary<string,object>進行包裝,下面是RouteValueDictionary拆解   

public RouteValueDictionary(object values)
{
    this._dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
    this.AddValues(values);
}
private void AddValues(object values)
{
    if (values != null)
    {
        foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values))
        {
            object obj2 = descriptor.GetValue(values);
            this.Add(descriptor.Name, obj2);
        }
    }
}

             我們來看看效果

                

五.Add(name,route)

             我們來看看這個Add方法:   

        public void Add(string name, RouteBase item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            if (!string.IsNullOrEmpty(name) && this._namedMap.ContainsKey(name))
            {
                object[] args = new object[] { name };
                //throw new ArgumentException(string.Format(CultureInfo.CurrentUICulture, System.Web.SR.GetString("RouteCollection_DuplicateName"), args), "name");
            }
            base.Add(item);
            if (!string.IsNullOrEmpty(name))
            {
                this._namedMap[name] = item;
            }
        }

 

             里面做了重復路由名稱驗證,Add方法的第二個參數是RouteBase,我們看看MapRoute方法里傳入給Add方法的參數是Route。Route是繼承於RouteBase,RouteBase是一個抽象類,這個類是為繼承類服務的,里面定義了GetRouteData和GetVirtualPath兩個抽象方法。

             GetRouteData:解析請求url,提取數據,如:/home/index 得到:controller/home,action/index提取得到的數據會包裝成RouteData

             GetVirtualPath:生成URL

六.RouteBase Route    

public abstract class RouteBase
{
    // Methods
    protected RouteBase();
    public abstract RouteData GetRouteData(HttpContextBase httpContext);
    public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
}

 

public class Route : RouteBase
{
    // Fields
    private ParsedRoute _parsedRoute;
    private string _url;
    private const string HttpMethodParameterName = "httpMethod";

    // Methods
    public Route(string url, IRouteHandler routeHandler);
    public Route(string url, RouteValueDictionary defaults, IRouteHandler routeHandler);
    public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler);
    public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler);
    public override RouteData GetRouteData(HttpContextBase httpContext);
    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
    protected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);
    private bool ProcessConstraints(HttpContextBase httpContext, RouteValueDictionary values, RouteDirection routeDirection);

    // Properties
    public RouteValueDictionary Constraints { get; set; }
    public RouteValueDictionary DataTokens { get; set; }
    public RouteValueDictionary Defaults { get; set; }
    public IRouteHandler RouteHandler { get; set; }
    public string Url { get; set; }
}

 

              Route添加了幾個屬性

                          Constraints:保存約束規則,最終保存為RouteValueDictionary

                          DataTokens:附加參數,指定controller的空間命名也放在這里。

                          Defaults:保存規則的默認值

                          url:規則URL

             我們來看看GetRouteData,我們看看RouteData routeData=RouteCollection.GetRouteData(context)。

public RouteData GetRouteData(HttpContextBase httpContext)
{
    if (httpContext == null)
    {
        throw new ArgumentNullException("httpContext");
    }
    if (httpContext.Request == null)
    {
        throw new ArgumentException(RoutingResources.RouteTable_ContextMissingRequest, "httpContext");
    }
    if (!this.RouteExistingFiles)
    {
        string appRelativeCurrentExecutionFilePath = httpContext.Request.AppRelativeCurrentExecutionFilePath;
        if (((appRelativeCurrentExecutionFilePath != "~/") && (this._vpp != null)) && (this._vpp.FileExists(appRelativeCurrentExecutionFilePath) || this._vpp.DirectoryExists(appRelativeCurrentExecutionFilePath)))
        {
            return null;
        }
    }
    using (this.GetReadLock())
    {
        foreach (RouteBase base2 in this)
        {
            RouteData routeData = base2.GetRouteData(httpContext);
            if (routeData != null)
            {
                return routeData;
            }
        }
    }
    return null;
}

 

             上述代碼中最后通過遞歸遍歷自己所有的路由規則,分別調用我們所有注冊在RouteTable.Routes--->RouteCollection。

             

             我們還注意到上面有這么一段: 

復制代碼
if (!this.RouteExistingFiles)
    {
        string appRelativeCurrentExecutionFilePath = httpContext.Request.AppRelativeCurrentExecutionFilePath;
        if (((appRelativeCurrentExecutionFilePath != "~/") && (this._vpp != null)) && (this._vpp.FileExists(appRelativeCurrentExecutionFilePath) || this._vpp.DirectoryExists(appRelativeCurrentExecutionFilePath)))
        {
            return null;
        }
    }
復制代碼

             RouteCollection 有這么一個屬性RouteExistingFiles.當為false時,就檢測請求的路徑地址是否己經存在文件或目錄,如果存在,則直接不走路由了,直接返回null,默認就是false,我們可以實驗一下。當然這里是忽略了根目錄的,不然默認我們 http://www.xxx.com/ 也不能訪問了。

復制代碼
public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
                new string[] { "MVCTest.Controllers" }
            );
        }
復制代碼

             這里是默認的路由注冊,按理說我們訪問 home 時,會去到 home controller 的 index,但是我們在在項目里加一個 home 目錄,如下圖。

             我們再訪問:http://localhost:2144/home/ 我們發現,無法找到該資源,也就是檢測到home這個目錄存在時,就不走路由了。

 

            為尊重原創,本文的編寫參考了以下博文和文章:

                       程序園: http://www.cnblogs.com/lindaohui/archive/2012/08/31/2664047.html

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM