MVC 路由源码解析

//到页面底部下载源,配合效果跟好。
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { RouteConfig.RegisterRoutes(RouteTable.Routes); //调用RouteConfig类的RegisterRoutes方法注册路由 //RouteTable.Routes是一个储存Route的集合 } }

我们转到RouteConfig类的定义

    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            //忽略可用的URL
           // 例如: routes.IgnoreRoute("Home/Index");
            //在浏览器输入 http://localhost:47662/Home/Index 
            //得到:HTTP 错误 404.0 - Not Found 的提示

            //默认值
             var defaults = new RouteValueDictionary { { "controller", "Home" }, { "action","Index" } };


             //路由对象
               Route R = new Route("{controller}/{action}/{*id}", null, null, null, new MvcRouteHandler());
            //加入集合
            RouteTable.Routes.Add("Default",R);

        }
    }

转到:Route 类去看怎么初始化构函数的

namespace MyRotue
{
    //这个类不是系统的,是自己写的
    public class Route : RouteBase
    {    
        private ParsedRoute _parsedRoute;
        private string _url;
    
        public Route(string url, IRouteHandler routeHandler)
        {
            this.Url = url;
            this.RouteHandler = routeHandler;
        }

        public Route(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
        {
            this.Url = url;
            this.Defaults = defaults;
            this.RouteHandler = routeHandler;
        }

        public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
        {
            this.Url = url;
            this.Defaults = defaults;
            this.Constraints = constraints;
            this.RouteHandler = routeHandler;
        }

        public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
        {
            this.Url = url; //URL模板
            this.Defaults = defaults; //默认值
            this.Constraints = constraints; //约束
            this.DataTokens = dataTokens; //获取或设置传递到路由处理程序但未用于确定该路由是否匹配 URL 模式的自定义值。
            this.RouteHandler = routeHandler; //路由处理请求的对象


            //当初始化 this.Url 模板时:将给Url赋值

        }

        public string Url
        {
            get
            {
                return (this._url ?? string.Empty);
            }
            set
            {
                //this._parsedRoute:解析完的路由,把模板传入RouteParser(路由解析器)类的Parse()方法中,
                //然后返回解析好了的路由。
                this._parsedRoute = RouteParser.Parse(value);
                this._url = value;
            }
        }
// 这不是完全代码目前还用不到下面的方法,就先不贴出来了 }
我们转到RouteParser.Parse(Value)中出看下怎么解析的
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MyRotue
{
    public class RouteParser
    {
     

        public static ParsedRoute Parse(string routeUrl)
        {
           // -------------------------------
            // 这是第一步,先进入这个方法

            //判断routeUrl 是否为空
            if (routeUrl == null)
            {
                routeUrl = string.Empty;
            }

              //判断是不是无效路由
            if (IsInvalidRouteUrl(routeUrl))
            {
                throw new ArgumentException("出错了");
            }

            //拆分URL路径,如果routeUrl:Home/Index/Id
            //将被拆分成:[Home],[/],[Index],[/],[Index] 这样的数组
            IList<string> pathSegments = SplitUrlToPathSegmentStrings(routeUrl);

            //验证URL部分
            Exception exception = ValidateUrlParts(pathSegments);
         
            if (exception != null)
            {
                throw exception;
            }
            //分类
            return new ParsedRoute(SplitUrlToPathSegments(pathSegments));
        }


//这不是全部代码
}

我们转到IsInvalidRouteUrl(routeUrl)这个方法,看到是怎么判断的
internal static bool IsInvalidRouteUrl(string routeUrl)
{

    //检查开头字符串,是否有 "~" 和  "/"
    if (!routeUrl.StartsWith("~", StringComparison.Ordinal) && !routeUrl.StartsWith("/", StringComparison.Ordinal))
    {
        //在判断字符串有没有 "?"
        return (routeUrl.IndexOf(?) != -1);
    }
    return true;
}

转到:SplitUrlToPathSegmentStrings()这个方法

      internal static IList<string> SplitUrlToPathSegmentStrings(string url)
        {
            List<string> list = new List<string>();
            if (!string.IsNullOrEmpty(url))
            {
                int index;
                for (int i = 0; i < url.Length; i = index + 1)
                {
                    //获取"/" 在 url中的位置
                    index = url.IndexOf(/, i);
                    

                  
                    if (index == -1)
                    {
                       // 获取最后一个参数
                        string str = url.Substring(i);
                        if (str.Length > 0)
                        {
                            list.Add(str);
                        }
                        return list;
                    }
                    //获取参数
                    string item = url.Substring(i, index - i);
                    if (item.Length > 0)
                    {
                        list.Add(item);
                    }
                    list.Add("/");
                }
            }
            return list;
        }

转到 ValidateUrlParts()方法

    private static Exception ValidateUrlParts(IList<string> pathSegments)
        {
            //使用参数名称
            HashSet<string> usedParameterNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            bool? nullable = null;
           

            foreach (string str in pathSegments)
            {
                bool flag2;         
                if (!nullable.HasValue)
                {
                                        //判断是不是分割符
                    nullable = new bool?(IsSeparator(str));
                    flag2 = nullable.Value;
                }
                else
                {
                  
                    flag2 = IsSeparator(str);
                    //如果连续有两个"/"就执行异常 
                    if (flag2 && nullable.Value)
                    {
                        return new ArgumentException("出错了");
                    }
                    nullable = new bool?(flag2);
                }
                if (!flag2)
                {
                    Exception exception;
                                                            //解析URL分类
                    IList<PathSubsegment> pathSubsegments = ParseUrlSegment(str, out exception);
                    if (exception != null)
                    {
                        return exception;
                    }

                    //验证URL分类
                    exception = ValidateUrlSegment(pathSubsegments, usedParameterNames, str);
                    if (exception != null)
                    {
                        return exception;
                    }
                 
                }
            }
            return null;
        }
转到:IsSeparator
 internal static bool IsSeparator(string s)
        {
            return string.Equals(s, "/", StringComparison.Ordinal);
        }
转到:ParseUrlSegment()
private static IList<PathSubsegment> ParseUrlSegment(string segment, out Exception exception)
{
    int startIndex = 0;
    List<PathSubsegment> list = new List<PathSubsegment>();
  

        // 判断是不是区域
        int num2 = IndexOfFirstOpenParameter(segment, startIndex);
        if (num2 == -1)
        {
               //得到字符串
            string str = GetLiteral(segment.Substring(startIndex));
            if (str == null)
            {
                exception =  new ArgumentException("出错了");

                return null;
            }
            if (str.Length > 0)
            {
             list.Add(new LiteralSubsegment(str));
             exception = null;
             return list;
            }
         
        }
        //检查字符串,是否有“}”,没有就报错
   
        if (num2 >=1)
        {
            exception = new ArgumentException("出错了");

            return null;
        }
      
        int index = segment.IndexOf(}, num2 + 1);
        if (index != segment.Length - 1)
        {
            exception = new ArgumentException("出错了");
            return null;
        }
    //获取参数名字
        string parameterName = segment.Substring(num2 + 1, (index - num2) - 1);
        list.Add(new ParameterSubsegment(parameterName));

        exception = null;
        return list;
}

转到:IndexOfFirstOpenParameter
private static int IndexOfFirstOpenParameter(string segment, int startIndex)
{
    
        startIndex = segment.IndexOf({, startIndex);     
        return startIndex;
    
}

转到:GetLiteral


private static string GetLiteral(string segmentLiteral)
{
    string str = segmentLiteral.Replace("{{", "").Replace("}}", "");
    if (!str.Contains("{") && !str.Contains("}"))
    {
        return segmentLiteral.Replace("{{", "{").Replace("}}", "}");
    }
    return null;
}

List<PathSubsegment> list = new List<PathSubsegment>();

我们来看看PathSubsegment类吧,

  internal abstract class PathSubsegment
    {

        protected PathSubsegment() { }
    }




    //文字路劲部分,存放区域的,如:JB/{Home}/{Index}/{Id},他存放的就是JB
    internal sealed class LiteralSubsegment : PathSubsegment
    {
        // Methods
        public LiteralSubsegment(string literal)
        {
            this.Literal = literal;
        }
        // Properties
        public string Literal
        {
            get;
            set;
        }

    }
}



 //参数子段,存放参数的
    internal sealed class ParameterSubsegment : PathSubsegment
    {
        // Methods
        public ParameterSubsegment(string parameterName)
        {
            if (parameterName.StartsWith("*", StringComparison.Ordinal))
            {
                this.ParameterName = parameterName.Substring(1);
                this.IsCatchAll = true;
            }
            else
            {
                this.ParameterName = parameterName;
            }
        }
        // Properties
        public bool IsCatchAll {  get; private set; }
        public string ParameterName {get;  private set; }
    }


//存放 "/"符号的

internal sealed class SeparatorPathSegment : PathSegment
{
// Methods
public SeparatorPathSegment() { }
}



这两个都是他的子类
 

转到: ValidateUrlSegment

private static Exception ValidateUrlSegment(IList<PathSubsegment> pathSubsegments, HashSet<string> usedParameterNames, string pathSegment)
{

    Type type = null;
    foreach (PathSubsegment subsegment in pathSubsegments)
    {
        if ((type != null) && (type == subsegment.GetType()))
        {
            return new ArgumentException("出错了");
        }
        type = subsegment.GetType();
        if (!(subsegment is LiteralSubsegment))
        {
            ParameterSubsegment subsegment3 = subsegment as ParameterSubsegment;
            if (subsegment3 != null)
            {
                string parameterName = subsegment3.ParameterName;
               
                //判断里面有没有"/"
                if (!IsValidParameterName(parameterName))
                {
                    return new ArgumentException("出错了");
                }
                //判断有没有重复
                if (usedParameterNames.Contains(parameterName))
                {
                   
                    return new ArgumentException("出错了");

                }
                usedParameterNames.Add(parameterName);
            }
        }
    }
  
    return null;
}

 

现在转到:   return new ParsedRoute(SplitUrlToPathSegments(pathSegments));

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;

namespace MyRotue
{
    public class ParsedRoute
    {


        public ParsedRoute(IList<PathSegment> pathSegments)
        {
            this.PathSegments = pathSegments;
        }

        public RouteValueDictionary Match(string virtualPath, RouteValueDictionary defaultValues)
        {
            //拆分URL
            IList<string> source = RouteParser.SplitUrlToPathSegmentStrings(virtualPath);
            if (defaultValues == null)
            {
                defaultValues = new RouteValueDictionary();
            }

            //匹配值
            RouteValueDictionary matchedValues = new RouteValueDictionary();
            bool flag = false;
            bool flag2 = false;

            for (int i = 0; i < this.PathSegments.Count; i++)
            {
                PathSegment segment = this.PathSegments[i];
                if (source.Count <= i)
                {
                    flag = true;
                }
                string a = flag ? null : source[i];
                if (segment is SeparatorPathSegment)
                {
                    if (!flag && !string.Equals(a, "/", StringComparison.Ordinal))
                    {
                        return null;
                    }
                }
                else
                {
                    ContentPathSegment contentPathSegment = segment as ContentPathSegment;
                    if (contentPathSegment != null)
                    {
                        if (contentPathSegment.IsCatchAll)
                        {
                            this.MatchCatchAll(contentPathSegment, source.Skip<string>(i), defaultValues, matchedValues);
                            flag2 = true;
                        }
                        else if (!this.MatchContentPathSegment(contentPathSegment, a, defaultValues, matchedValues))
                        {
                            return null;
                        }
                    }
                }
            }
            if (!flag2 && (this.PathSegments.Count < source.Count))
            {
                for (int j = this.PathSegments.Count; j < source.Count; j++)
                {
                    if (!RouteParser.IsSeparator(source[j]))
                    {
                        return null;
                    }
                }
            }
            if (defaultValues != null)
            {
                foreach (KeyValuePair<string, object> pair in defaultValues)
                {
                    if (!matchedValues.ContainsKey(pair.Key))
                    {
                        matchedValues.Add(pair.Key, pair.Value);
                    }
                }
            }
            return matchedValues;
        }

        private void MatchCatchAll(ContentPathSegment contentPathSegment, IEnumerable<string> remainingRequestSegments, RouteValueDictionary defaultValues, RouteValueDictionary matchedValues)
        {
            object obj2;
            string str = string.Join(string.Empty, remainingRequestSegments.ToArray<string>());
            ParameterSubsegment subsegment = contentPathSegment.Subsegments[0] as ParameterSubsegment;
            if (str.Length > 0)
            {
                obj2 = str;
            }
            else
            {
                defaultValues.TryGetValue(subsegment.ParameterName, out obj2);
            }
            matchedValues.Add(subsegment.ParameterName, obj2);
        }


        private bool MatchContentPathSegment(ContentPathSegment routeSegment, string requestPathSegment, RouteValueDictionary defaultValues, RouteValueDictionary matchedValues)
        {
            if (string.IsNullOrEmpty(requestPathSegment))
            {
                if (routeSegment.Subsegments.Count <= 1)
                {
                    object obj2;
                    ParameterSubsegment subsegment = routeSegment.Subsegments[0] as ParameterSubsegment;
                    if (subsegment == null)
                    {
                        return false;
                    }
                    if (defaultValues.TryGetValue(subsegment.ParameterName, out obj2))
                    {
                        matchedValues.Add(subsegment.ParameterName, obj2);
                        return true;
                    }
                }
                return false;
            }

            int length = requestPathSegment.Length;
            int num2 = routeSegment.Subsegments.Count - 1;
            ParameterSubsegment subsegment2 = null;
            LiteralSubsegment subsegment3 = null;
            while (num2 >= 0)
            {
                int num3 = length;
                ParameterSubsegment subsegment4 = routeSegment.Subsegments[num2] as ParameterSubsegment;
                if (subsegment4 != null)
                {
                    subsegment2 = subsegment4;
                }
                else
                {
                    LiteralSubsegment subsegment5 = routeSegment.Subsegments[num2] as LiteralSubsegment;
                    if (subsegment5 != null)
                    {
                        subsegment3 = subsegment5;
                        int startIndex = length - 1;
                        if (subsegment2 != null)
                        {
                            startIndex--;
                        }
                        if (startIndex < 0)
                        {
                            return false;
                        }
                        int num5 = requestPathSegment.LastIndexOf(subsegment5.Literal, startIndex, StringComparison.OrdinalIgnoreCase);
                        if (num5 == -1)
                        {
                            return false;
                        }
                        if ((num2 == (routeSegment.Subsegments.Count - 1)) && ((num5 + subsegment5.Literal.Length) != requestPathSegment.Length))
                        {
                            return false;
                        }
                        num3 = num5;
                    }
                }
                if ((subsegment2 != null) && (((subsegment3 != null) && (subsegment4 == null)) || (num2 == 0)))
                {
                    int num6;
                    int num7;
                    if (subsegment3 == null)
                    {
                        if (num2 == 0)
                        {
                            num6 = 0;
                        }
                        else
                        {
                            num6 = num3;
                        }
                        num7 = length;
                    }
                    else if ((num2 == 0) && (subsegment4 != null))
                    {
                        num6 = 0;
                        num7 = length;
                    }
                    else
                    {
                        num6 = num3 + subsegment3.Literal.Length;
                        num7 = length - num6;
                    }
                    string str = requestPathSegment.Substring(num6, num7);
                    if (string.IsNullOrEmpty(str))
                    {
                        return false;
                    }
                    matchedValues.Add(subsegment2.ParameterName, str);
                    subsegment2 = null;
                    subsegment3 = null;
                }
                length = num3;
                num2--;
            }
          
            return true;
        }

        private IList<PathSegment> PathSegments { get; set; }


 

    }
}

好了,差不多了,你看到这里,然后再下载源码,调试一遍,就因该了路由匹配流程了。

 

 

 



源码中肯定,有很多错误,请指教。

源码:http://files.cnblogs.com/liek/MyRotue.rar


MVC 路由源码解析,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。