Asp.net mvc5 解析route源码实现自己的route系统
url route
路由系统的责任是找到匹配的路由,创建路由数据,并将请求分配给一个处理程序。
选择动作是 MVC 的处理程序的实现细节。它使用路由数据和从传入请求其他信息来选择要执行的操作
实例
源码:http://pan.baidu.com/s/1i3lfbaH
具体实现
//存放url 路径片段 public abstract class PathSegment { // Methods protected PathSegment() { } }
// 主要存放 url template 的 "/" //比如{controller}/{action}中 "/" public sealed class SeparatorPathSegment : PathSegment { // Methods public SeparatorPathSegment() { } }
//存放 url tamplate subpathsegment //比如A{controller}/{action}中 "controller" 或 action 或 A public abstract class PathSubsegment { // Methods protected PathSubsegment() {} }
//存放 url tamplate subpathsegment //比如A{controller}/{action}中 "controller" 或 action public sealed class ParameterSubsegment : PathSubsegment { public ParameterSubsegment(string parameterName) { if (parameterName.StartsWith("*", StringComparison.Ordinal)) { this.ParameterName = parameterName.Substring(1); this.IsCatchAll = true; } else { this.ParameterName = parameterName; } } public bool IsCatchAll { get; private set; } public string ParameterName { get; private set; } }
//存放 url tamplate subpathsegment //比如A{controller}/{action}中 A public sealed class LiteralSubsegment : PathSubsegment { // Methods public LiteralSubsegment(string literal) { this.Literal = literal; } // Properties public string Literal { get; private set; } }
以上是存放被分割的url 片段的类
myRoute 类
public class MyRoute: RouteBase { private ParsedRoute _parsedRoute; private string _url; private const string HttpMethodParameterName = "httpMethod"; public MyRoute(string url, IRouteHandler routeHandler) { this.Url = url; this.RouteHandler = routeHandler; } public MyRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler) { this.Url = url; this.Defaults = defaults; this.RouteHandler = routeHandler; } public MyRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler) { this.Url = url; this.Defaults = defaults; this.Constraints = constraints; this.RouteHandler = routeHandler; } public MyRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler) { this.Url = url; this.Defaults = defaults; this.Constraints = constraints; this.DataTokens = dataTokens; this.RouteHandler = routeHandler; } //获取routedate public override RouteData GetRouteData(HttpContextBase httpContext) { string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo; RouteValueDictionary values = this._parsedRoute.Match(virtualPath, this.Defaults); if (values == null) { return null; } RouteData data = new RouteData(this, this.RouteHandler); foreach (KeyValuePair<string, object> pair in values) { data.Values.Add(pair.Key, pair.Value); } if (this.DataTokens != null) { foreach (KeyValuePair<string, object> pair2 in this.DataTokens) { data.DataTokens[pair2.Key] = pair2.Value; } } return data; } public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) { throw new NotImplementedException(); } public RouteValueDictionary Constraints { get; set; } public RouteValueDictionary DataTokens { get; set; } public RouteValueDictionary Defaults { get; set; } public IRouteHandler RouteHandler { get; set; } public string Url { get { return (this._url ?? string.Empty); } set { //解析路由也就是分割url this._parsedRoute = RouteParser.Parse(value); this._url = value; } } }
RouteParesed 类负责解析url 模板
关键地方都有注释
public static class RouteParser { // 解析route uel public static ParsedRoute Parse(string routeUrl) { if (routeUrl == null) { routeUrl = string.Empty; } //判断网址是否有效 if (IsInvalidRouteUrl(routeUrl)) { // throw new ArgumentException(SR.GetString("Route_InvalidRouteUrl"), "routeUrl"); } //根据/拆分网址 IList<string> pathSegments = SplitUrlToPathSegmentStrings(routeUrl); //验证部分url Exception exception = ValidateUrlParts(pathSegments); if (exception != null) { throw exception; } //拆分的url加入到PathSegments return new ParsedRoute(SplitUrlToPathSegments(pathSegments)); } public static Exception ValidateUrlParts(IList<string> pathSegments) { HashSet<string> usedParameterNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase); bool? nullable = null; bool flag = false; foreach (string str in pathSegments) { bool flag2; if (flag) { //return new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.GetString("Route_CatchAllMustBeLast"), new object[0]), "routeUrl"); } if (!nullable.HasValue) { nullable = new bool?(IsSeparator(str)); flag2 = nullable.Value; } else { flag2 = IsSeparator(str); if (flag2 && nullable.Value) { // return new ArgumentException(SR.GetString("Route_CannotHaveConsecutiveSeparators"), "routeUrl"); } nullable = new bool?(flag2); } if (!flag2) { Exception exception2; IList<PathSubsegment> pathSubsegments = ParseUrlSegment(str, out exception2); if (exception2 != null) { return exception2; } exception2 = ValidateUrlSegment(pathSubsegments, usedParameterNames, str); if (exception2 != null) { return exception2; } flag = pathSubsegments.Any<PathSubsegment>(seg => (seg is ParameterSubsegment) && ((ParameterSubsegment)seg).IsCatchAll); } } return null; } private static Exception ValidateUrlSegment(IList<PathSubsegment> pathSubsegments, HashSet<string> usedParameterNames, string pathSegment) { bool flag = false; Type type = null; foreach (PathSubsegment subsegment in pathSubsegments) { if ((type != null) && (type == subsegment.GetType())) { //return new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.GetString("Route_CannotHaveConsecutiveParameters"), new object[0]), "routeUrl"); } type = subsegment.GetType(); if (!(subsegment is LiteralSubsegment)) { ParameterSubsegment subsegment2 = subsegment as ParameterSubsegment; if (subsegment2 != null) { string parameterName = subsegment2.ParameterName; if (subsegment2.IsCatchAll) { flag = true; } if (!IsValidParameterName(parameterName)) { object[] args = new object[] { parameterName }; //return new ArgumentException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_InvalidParameterName"), args), "routeUrl"); } if (usedParameterNames.Contains(parameterName)) { object[] objArray2 = new object[] { parameterName }; //return new ArgumentException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_RepeatedParameter"), objArray2), "routeUrl"); } usedParameterNames.Add(parameterName); } } } if (flag && (pathSubsegments.Count != 1)) { //return new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.GetString/("Route_CannotHaveCatchAllInMultiSegment"), new object[0]), "routeUrl"); } return null; } private static bool IsValidParameterName(string parameterName) { if (parameterName.Length == 0) { return false; } for (int i = 0; i < parameterName.Length; i++) { switch (parameterName[i]) { case '/': case '{': case '}': return false; } } return true; } public static bool IsInvalidRouteUrl(string routeUrl) { if (!routeUrl.StartsWith("~", StringComparison.Ordinal) && !routeUrl.StartsWith("/", StringComparison.Ordinal)) { return (routeUrl.IndexOf('?') != -1); } return true; } //判断是否是分隔符 public static bool IsSeparator(string s) { return string.Equals(s, "/", StringComparison.Ordinal); } // public static IList<PathSegment> SplitUrlToPathSegments(IList<string> urlParts) { List<PathSegment> list = new List<PathSegment>(); foreach (string str in urlParts) { //判读是否是分割符 if (IsSeparator(str)) { //如果是分隔符/就加入pathsegment list.Add(new SeparatorPathSegment()); } else { Exception exception; //解析 segment IList<PathSubsegment> subsegments = ParseUrlSegment(str, out exception); list.Add(new ContentPathSegment(subsegments)); } } return list; } public static IList<PathSubsegment> ParseUrlSegment(string segment, out Exception exception) { int startIndex = 0; List<PathSubsegment> list = new List<PathSubsegment>(); while (startIndex < segment.Length) { //获得{索引位置 //比如{controller} num2=0 int num2 = IndexOfFirstOpenParameter(segment, startIndex); if (num2 == -1) { //获取LiteralSubsegment string str3 = GetLiteral(segment.Substring(startIndex)); if (str3 == null) { object[] args = new object[] { segment }; //exception = new ArgumentException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_MismatchedParameter"), args), "routeUrl"); // return null; } if (str3.Length > 0) { //加入获取LiteralSubsegment list.Add(new LiteralSubsegment(str3)); } break; } //获得}索引位置 int index = segment.IndexOf('}', num2 + 1); if (index == -1) { object[] objArray2 = new object[] { segment }; //exception = new ArgumentException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_MismatchedParameter"), objArray2), "routeUrl"); //return null; } //获取LiteralSubsegment string string literal = GetLiteral(segment.Substring(startIndex, num2 - startIndex)); if (literal == null) { object[] objArray3 = new object[] { segment }; //exception = new ArgumentException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_MismatchedParameter"), objArray3), "routeUrl"); //return null; } if (literal.Length > 0) { list.Add(new LiteralSubsegment(literal)); } //获取参数名称 string parameterName = segment.Substring(num2 + 1, (index - num2) - 1); //加入ParameterSubsegment list.Add(new ParameterSubsegment(parameterName)); //继续解析segment ,如果startIndex= segment.length表示解析完毕 startIndex = index + 1; } exception = null; return list; } //去除 url template 中的{}获得文字 public static string GetLiteral(string segmentLiteral) { string str = segmentLiteral.Replace("{{", "").Replace("}}", ""); if (!str.Contains("{") && !str.Contains("}")) { return segmentLiteral.Replace("{{", "{").Replace("}}", "}"); } return null; } private static int IndexOfFirstOpenParameter(string segment, int startIndex) { while (true) { startIndex = segment.IndexOf('{', startIndex); if (startIndex == -1) { return -1; } if (((startIndex + 1) == segment.Length) || (((startIndex + 1) < segment.Length) && (segment[startIndex + 1] != '{'))) { return startIndex; } startIndex += 2; } } public 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) { index = url.IndexOf('/', i); if (index == -1) { string str2 = url.Substring(i); if (str2.Length > 0) { list.Add(str2); } return list; } string item = url.Substring(i, index - i); if (item.Length > 0) { list.Add(item); } list.Add("/"); } } return list; } } }
ParsedRoute 类负责解析请求的url
并且返回匹配url的值也就是route的值
关键地方有注释
public class ParsedRoute { public ParsedRoute(IList<PathSegment> pathSegments) { this.PathSegments = pathSegments; } private static string EscapeReservedCharacters(Match m) { return ("%" + Convert.ToUInt16(m.Value[0]).ToString("x2", CultureInfo.InvariantCulture)); } private static bool ForEachParameter(IList<PathSegment> pathSegments, Func<ParameterSubsegment, bool> action) { for (int i = 0; i < pathSegments.Count; i++) { PathSegment segment = pathSegments[i]; if (!(segment is SeparatorPathSegment)) { ContentPathSegment segment2 = segment as ContentPathSegment; if (segment2 != null) { foreach (PathSubsegment subsegment in segment2.Subsegments) { if (!(subsegment is LiteralSubsegment)) { ParameterSubsegment arg = subsegment as ParameterSubsegment; if ((arg != null) && !action(arg)) { return false; } } } } } } return true; } private static ParameterSubsegment GetParameterSubsegment(IList<PathSegment> pathSegments, string parameterName) { ParameterSubsegment foundParameterSubsegment = null; ForEachParameter(pathSegments, delegate (ParameterSubsegment parameterSubsegment) { if (string.Equals(parameterName, parameterSubsegment.ParameterName, StringComparison.OrdinalIgnoreCase)) { foundParameterSubsegment = parameterSubsegment; return false; } return true; }); return foundParameterSubsegment; } private static bool IsParameterRequired(ParameterSubsegment parameterSubsegment, RouteValueDictionary defaultValues, out object defaultValue) { if (parameterSubsegment.IsCatchAll) { defaultValue = null; return false; } return !defaultValues.TryGetValue(parameterSubsegment.ParameterName, out defaultValue); } private static bool IsRoutePartNonEmpty(object routePart) { string str = routePart as string; if (str != null) { return (str.Length > 0); } return ((int)routePart > null); } public RouteValueDictionary Match(string virtualPath, RouteValueDictionary defaultValues) { //分割virtualPath 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; //判断是否为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) { //判断requestPathSegment 是否为空 //如果为空直接用默认值 if (string.IsNullOrEmpty(requestPathSegment)) { if (routeSegment.Subsegments.Count <= 1) { object obj2; ParameterSubsegment subsegment3 = routeSegment.Subsegments[0] as ParameterSubsegment; if (subsegment3 == null) { return false; } if (defaultValues.TryGetValue(subsegment3.ParameterName, out obj2)) { matchedValues.Add(subsegment3.ParameterName, obj2); return true; } } return false; } //获取pathsegment.length 长度 int length = requestPathSegment.Length; int num2 = routeSegment.Subsegments.Count - 1; ParameterSubsegment subsegment = null; LiteralSubsegment subsegment2 = null; //循环routeSegment while (num2 >= 0) { int num3 = length; //判断是否为ParameterSubsegment ParameterSubsegment subsegment4 = routeSegment.Subsegments[num2] as ParameterSubsegment; if (subsegment4 != null) { subsegment = subsegment4; } else { //判断是否为LiteralSubsegment LiteralSubsegment subsegment5 = routeSegment.Subsegments[num2] as LiteralSubsegment; if (subsegment5 != null) { subsegment2 = subsegment5; int startIndex = length - 1; //判断索引是否为空 if (subsegment != null) { //startIndex-1 表示从倒数第二个字符开始索引 startIndex--; } if (startIndex < 0) { return false; } //搜索指定LiteralSubsegment int num5 = requestPathSegment.LastIndexOf(subsegment5.Literal, startIndex, StringComparison.OrdinalIgnoreCase); //num5=-1 表示没有索引到LiteralSubsegment if (num5 == -1) { return false; } if ((num2 == (routeSegment.Subsegments.Count - 1)) && ((num5 + subsegment5.Literal.Length) != requestPathSegment.Length)) { return false; } num3 = num5; } } // subsegment!=null subsegment2!=null subsegment4==null num2=0 //表示找到最后一LiteralSubsegment if ((subsegment != null) && (((subsegment2 != null) && (subsegment4 == null)) || (num2 == 0))) { int num6; int num7; if (subsegment2 == null) { if (num2 == 0) { num6 = 0; } else { num6 = num3; } num7 = length; } else if ((num2 == 0) && (subsegment4 != null)) { num6 = 0; num7 = length; } else { //Substring起始位置 num6 = num3 + subsegment2.Literal.Length; //截取的字符个数 num7 = length - num6; } string str = requestPathSegment.Substring(num6,num7); if (string.IsNullOrEmpty(str)) { return false; } //加入匹配值 matchedValues.Add(subsegment.ParameterName, str); subsegment = null; subsegment2 = null; } length = num3; num2--; } if (length != 0) { return (routeSegment.Subsegments[0] is ParameterSubsegment); } return true; } private static bool RoutePartsEqual(object a, object b) { string str = a as string; string str2 = b as string; if ((str != null) && (str2 != null)) { return string.Equals(str, str2, StringComparison.OrdinalIgnoreCase); } if ((a != null) && (b != null)) { return a.Equals(b); } return (a == b); } private static string UrlEncode(string str) { return Regex.Replace(Uri.EscapeUriString(str), "([#;?:@&=+$,])", new MatchEvaluator(ParsedRoute.EscapeReservedCharacters)); } // Properties private IList<PathSegment> PathSegments { get; set; } } }
以上是route的大概流程
(1)先通过url tamplate 解析 url
(2) 解析请求的url 比较匹配赋值
(3) 再根据匹配到的值映射到相应的controller 和 Action 进行处理