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