• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

asp.netmvc源码分析-Route的GetRouteData

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

我知道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 中取出来转化为字符串,最后在用正则表达式来验证我们的值是否通过。
好,现在让我们来看看this._parsedRoute.Match(virtualPath, this.Defaults);这个方法是然后获取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}。
其中RouteParser的Parse方法如下:

[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 

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
asp.net正确获取当前URL发布时间:2022-07-10
下一篇:
July4thLinks:ASP.NET,ASP.NETAJAX,VisualStudio,SilverlightandIIS7发布时间:2022-07-10
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap