在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
我知道Route这里东西应该算路由,这里把它放到mvc里面有些不怎么合适,但是我想大家多数遇到路由都是在mvc的时候吧.首先我们还是来看看GetRouteData方法吧 [csharp] 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); if (!this.ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest)) { return null; } 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 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); if (!this.ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest)) { return null; } 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; } 我还是沿用以前的思路,已一个demo来便说明吧,现在假设我的路由信息是: routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.IgnoreRoute("{*favicon}", new { favicon = @"(.*/)?favicon.ico(/.*)?" }); routes.MapRoute( "Default", // 路由名称 "{controller}/{action}/{id}", // 带有参数的 URL new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // 参数默认值 new { controller="([A-Za-z])*" }, new string[] { "MvcApp.Controllers" } );
我们知道httpContext.Request.AppRelativeCurrentExecutionFilePath的返回值都是以~/打头的,这里httpContext.Request.PathInfo为空,多数情况下该属性也是空的,所以这里的virtualPath=Home/index。 有关MapRoute的代码可以参照 [csharp] public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) { if (routes == null) { throw new ArgumentNullException("routes"); } if (url == null) { throw new ArgumentNullException("url"); } Route route = new Route(url, new MvcRouteHandler()) { Defaults = new RouteValueDictionary(defaults), Constraints = new RouteValueDictionary(constraints), DataTokens = new RouteValueDictionary() }; if ((namespaces != null) && (namespaces.Length > 0)) { route.DataTokens["Namespaces"] = namespaces; } routes.Add(name, route); return route; } public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) { if (routes == null) { throw new ArgumentNullException("routes"); } if (url == null) { throw new ArgumentNullException("url"); } Route route = new Route(url, new MvcRouteHandler()) { Defaults = new RouteValueDictionary(defaults), Constraints = new RouteValueDictionary(constraints), DataTokens = new RouteValueDictionary() }; if ((namespaces != null) && (namespaces.Length > 0)) { route.DataTokens["Namespaces"] = namespaces; } routes.Add(name, route); return route; } 首先调用_parsedRoute.Match(virtualPath, this.Defaults)获取一个RouteValueDictionary ,至于这个方法的具体实现放到后面来说,然后实例化一个RouteData ,并且把先前的RouteValueDictionary的值添加到先前实例化的 RouteData中,如果DataTokens有元素的话也加入到RouteData的DataTokens中来。不过这个过程有个约束的处理 if (!this.ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest)) { return null; } 其中RouteDirection的定义如下: 1 public enum RouteDirection 2 { 3 IncomingRequest, 4 UrlGeneration 5 } 约束检查失败而返回null,现在我们来看看ProcessConstraints方法: private bool ProcessConstraints(HttpContextBase httpContext, RouteValueDictionary values, RouteDirection routeDirection) { if (this.Constraints != null) { foreach (KeyValuePair<string, object> pair in this.Constraints) { if (!this.ProcessConstraint(httpContext, pair.Value, pair.Key, values, routeDirection)) { return false; } } } return true; } 如果Constraints有元素,依次检查每个成员,检查方法主要是调用ProcessConstraint方法,
[csharp] protected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) { object obj2; IRouteConstraint constraint2 = constraint as IRouteConstraint; if (constraint2 != null) { return constraint2.Match(httpContext, this, parameterName, values, routeDirection); } string str = constraint as string; if (str == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_ValidationMustBeStringOrCustomConstraint"), new object[] { parameterName, this.Url })); } values.TryGetValue(parameterName, out obj2); string input = Convert.ToString(obj2, CultureInfo.InvariantCulture); string pattern = "^(" + str + ")$"; return Regex.IsMatch(input, pattern, RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.IgnoreCase); } protected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) { object obj2; IRouteConstraint constraint2 = constraint as IRouteConstraint; if (constraint2 != null) { return constraint2.Match(httpContext, this, parameterName, values, routeDirection); } string str = constraint as string; if (str == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_ValidationMustBeStringOrCustomConstraint"), new object[] { parameterName, this.Url })); } values.TryGetValue(parameterName, out obj2); string input = Convert.ToString(obj2, CultureInfo.InvariantCulture); string pattern = "^(" + str + ")$"; return Regex.IsMatch(input, pattern, RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.IgnoreCase); } 这里首先检查我们的约束类型,如果它是IRouteConstraint那么就直接调用它的Match方法,约束不是IRouteConstraint那么就转化为字符串,再把约束验证的值从RouteValueDictionary 中取出来转化为字符串,最后在用正则表达式来验证我们的值是否通过。 [csharp] public RouteValueDictionary Match(string virtualPath, RouteValueDictionary defaultValues) { 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; } public RouteValueDictionary Match(string virtualPath, RouteValueDictionary defaultValues) { 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; } 这里RouteParser的SplitUrlToPathSegmentStrings方法很简单,就是把url字符串按照”/“来分割 [csharp] view plaincopyprint?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) { 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; } 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) { 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; } 所以Match方法中的source 是成员是很好明白的,我的示例中它的值是: 在ParsedRoute的Match方法中用到了一个PathSegments属性。该属性定义为:private IList<PathSegment> PathSegments { get; set; }真正该改属性复制的是ParsedRoute的构造函数。而Route中的_parsedRoute的获取是在Url属性中 public string Url { get { return (this._url ?? string.Empty); } set { this._parsedRoute = RouteParser.Parse(value); this._url = value; } } 在我们这个例子中url的value={controller}/{action}/{id}。 [csharp] public static ParsedRoute Parse(string routeUrl) { if (routeUrl == null) { routeUrl = string.Empty; } if ((routeUrl.StartsWith("~", StringComparison.Ordinal) || routeUrl.StartsWith("/", StringComparison.Ordinal)) || (routeUrl.IndexOf('?') != -1)) { throw new ArgumentException(SR.GetString("Route_InvalidRouteUrl"), "routeUrl"); } IList<string> pathSegments = SplitUrlToPathSegmentStrings(routeUrl); Exception exception = ValidateUrlParts(pathSegments); if (exception != null) { throw exception; } return new ParsedRoute(SplitUrlToPathSegments(pathSegments)); } public static ParsedRoute Parse(string routeUrl) { if (routeUrl == null) { routeUrl = string.Empty; } if ((routeUrl.StartsWith("~", StringComparison.Ordinal) || routeUrl.StartsWith("/", StringComparison.Ordinal)) || (routeUrl.IndexOf('?') != -1)) { throw new ArgumentException(SR.GetString("Route_InvalidRouteUrl"), "routeUrl"); } IList<string> pathSegments = SplitUrlToPathSegmentStrings(routeUrl); Exception exception = ValidateUrlParts(pathSegments); if (exception != null) { throw exception; } return new ParsedRoute(SplitUrlToPathSegments(pathSegments)); } 在这里我们知道url不能以~ /打头,也不能含有?。这里的pathSegments也很好理解,其值: 这里的ValidateUrlParts主要就是验证这里的pathSegments集合,ValidateUrlParts这里的具体是怎么验证的很是复杂,这里就忽略了它吧。 有关SplitUrlToPathSegments的方法比较复杂,分为两部分,把urlParts中的"/" 变成SeparatorPathSegment对象作为站位,像{controller}这样转换为ContentPathSegment对象,其中它的subsegments是一个List<PathSubsegment>实例, [csharp] private static IList<PathSegment> SplitUrlToPathSegments(IList<string> urlParts) { List<PathSegment> list = new List<PathSegment>(); foreach (string str in urlParts) { if (IsSeparator(str)) { list.Add(new SeparatorPathSegment()); } else { Exception exception; IList<PathSubsegment> subsegments = ParseUrlSegment(str, out exception); list.Add(new ContentPathSegment(subsegments)); } } return list; } internal static bool IsSeparator(string s) { return string.Equals(s, "/", StringComparison.Ordinal); } private static IList<PathSubsegment> ParseUrlSegment(string segment, out Exception exception) { int startIndex = 0; List<PathSubsegment> list = new List<PathSubsegment>(); while (startIndex < segment.Length) { int num2 = IndexOfFirstOpenParameter(segment, startIndex); if (num2 == -1) { string str = GetLiteral(segment.Substring(startIndex)); if (str == null) { exception = new ArgumentException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_MismatchedParameter"), new object[] { segment }), "routeUrl"); return null; } if (str.Length > 0) { list.Add(new LiteralSubsegment(str)); } break; } int index = segment.IndexOf('}', num2 + 1); if (index == -1) { exception = new ArgumentException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_MismatchedParameter"), new object[] { segment }), "routeUrl"); return null; } string literal = GetLiteral(segment.Substring(startIndex, num2 - startIndex)); if (literal == null) { exception = new ArgumentException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_MismatchedParameter"), new object[] { segment }), "routeUrl"); return null; } if (literal.Length > 0) { list.Add(new LiteralSubsegment(literal)); } string parameterName = segment.Substring(num2 + 1, (index - num2) - 1); list.Add(new ParameterSubsegment(parameterName)); startIndex = index + 1; } exception = null; return list; } 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; } } private static string GetLiteral(string segmentLiteral) { string str = segmentLiteral.Replace("{{", "").Replace("}}", ""); if (!str.Contains("{") && !str.Contains("}")) { return segmentLiteral.Replace("{{", "{").Replace("}}", "}"); } return null; } internal sealed class LiteralSubsegment : PathSubsegment { // Methods public LiteralSubsegment(string literal) { this.Literal = literal; } // Properties public string Literal { get; private 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 ContentPathSegment : PathSegme |
请发表评论