首页 > 代码库 > MVC 路由源码解析

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