asp.net mvc源码分析-Route的GetRouteData

我知道Route这里东西应该算路由,这里把它放到mvc里面有些不怎么合适,但是我想大家多数遇到路由都是在mvc的时候吧.首先我们还是来看看GetRouteData方法吧

 我还是沿用以前的思路,已一个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" }
            );

   请求路径: http://localhost:7503/Home/index
我们知道httpContext.Request.AppRelativeCurrentExecutionFilePath的返回值都是以~/打头的,这里httpContext.Request.PathInfo为空,多数情况下该属性也是空的,所以这里的virtualPath=Home/index

有关MapRoute的代码可以参照

 

首先调用_parsedRoute.Match(virtualPath, this.Defaults)获取一个RouteValueDictionary ,至于这个方法的具体实现放到后面来说,然后实例化一个RouteData ,并且把先前的RouteValueDictionary的值添加到先前实例化的  RouteData中,如果DataTokens有元素的话也加入到RouteData的DataTokens中来。不过这个过程有个约束的处理

 if (!this.ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest))
    {
        return null;
    }

其中RouteDirection的定义如下:
    public enum RouteDirection
{
    IncomingRequest,
    UrlGeneration
}
约束检查失败而返回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方法,

 这里首先检查我们的约束类型,如果它是IRouteConstraint那么就直接调用它的Match方法,约束不是IRouteConstraint那 么就转化为字符串,再把约束验证的值从RouteValueDictionary 中取出来转化为字符串,最后在用正则表达式来验证我们的值是否通过。
好,现在让我们来看看this._parsedRoute.Match(virtualPath, this.Defaults);这个方法是然后获取RouteValueDictionary的:

 这里RouteParser的SplitUrlToPathSegmentStrings方法很简单,就是把url字符串按照”/“来分割

 所以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方法如下:

 在这里我们知道url不能以~ /打头,也不能含有?。这里的pathSegments也很好理解,其值:

这里的ValidateUrlParts主要就是验证这里的pathSegments集合,ValidateUrlParts这里的具体是怎么验证的很是复杂,这里就忽略了它吧。

有关SplitUrlToPathSegments的方法比较复杂,分为两部分,把urlParts中的"/" 变成SeparatorPathSegment对象作为站位,像{controller}这样转换为ContentPathSegment对象,其中它的 subsegments是一个List<PathSubsegment>实例,

 ParseUrlSegment方法主要就是获取代码是:

string parameterName = segment.Substring(num2 + 1, (index - num2) - 1);
        list.Add(new ParameterSubsegment(parameterName));

例如我们传进来的字符串是{controller},那么这里parameterName就会处理为controller。如果传入的参数没有{、}如Channel,那么ParseUrlSegment将处理为

   if (str.Length > 0)
                    {
                        list.Add(new LiteralSubsegment(str));
                    }
                    break;

现在是时候回到ParsedRoute的Match方法了,首先我们来看看这个方法用到的PathSegments是个什么东东,我的url是{controller}/{action}/{id}它对应的PathSegments如图:

着了我们看看几次循环的主要变量取值:

当前ContentPathSegment的IsCatchAll为false。那么我们现在主要关心的是MatchContentPathSegment方法了。

 这个方法就是真正把路由参数和路由的值给关联起来,如果参数requestPathSegment为null则参数值从defaultValues中获取,

 if (defaultValues.TryGetValue(subsegment.ParameterName, out obj2))
            {
                matchedValues.Add(subsegment.ParameterName, obj2);
                return true;
            }

否则从我们传递进来的requestPathSegment获取。

   string str = requestPathSegment.Substring(num6, num7);
            if (string.IsNullOrEmpty(str))
            {
                return false;
            }
            matchedValues.Add(subsegment2.ParameterName, str);

Match方法在结束之前会检查我们的defaultValues字典,把defaultValues中的key(matchedValues不存 在对应的key)加到matchedValues中来。整个match方法及结束了,结合前面的东西我们也就可以明白Route类的 GetRouteData方法了。
为了跟好的理解上面个方法,我这里再举一个demo:

 路由信息: routes.MapRoute("Default", "{ChannelName}/{action}", new { controller = "Home", action = "Index" });

请求路径:http://localhost:7503/men/index

调用Match方法的  virtualPath=men/index,

source取值:

PathSegments取值:

至于Match方法中的有一种路径是调用MatchCatchAll方法,

它要求contentPathSegment.IsCatchAll为true。从 ParameterSubsegment类的定义可以知道当且仅当传进来parameterName以*打头才是True。parameterName是从url中来的,也就是说url中要含有*,IsCatchAll才为true。在前面提到的ValidateUrlParts方法会验证url的,其主要代码如下:

private 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.CurrentUICulture, SR.GetString("Route_CatchAllMustBeLast"), new object[0]), "routeUrl");
        }
 

        if (!flag2)
        {
            Exception exception;
            IList<PathSubsegment> pathSubsegments = ParseUrlSegment(str, out exception);
 
            flag = pathSubsegments.Any<PathSubsegment>(seg => (seg is ParameterSubsegment) && ((ParameterSubsegment) seg).IsCatchAll);
        }
    }
    return null;
}
所以以上的那个MatchCatchAll执行的条件也很明显就是url中带有*号,并且是最后一个参数带有*,希望大家注意一下。大家看到这里相信对Route类的GetRouteData方法有个大致的了解了吧。

posted on   dz45693  阅读(5229)  评论(3编辑  收藏  举报

编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构

导航

< 2012年11月 >
28 29 30 31 1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 1
2 3 4 5 6 7 8
点击右上角即可分享
微信分享提示