zoukankan      html  css  js  c++  java
  • ASP.NET MVC 源码分析(二) —— 从 IRouteBuilder认识路由构建

      我们来看IRouteBuilder的定义:

        public interface IRouteBuilder
        {
            IRouter DefaultHandler { get; set; }
    
            IServiceProvider ServiceProvider { get; }
    
            IList<IRouter> Routes { get; }
    
            IRouter Build();
        }

    一个默认的IRouter对象,一个Build方法,一个IRouter集合和一个获取服务对象IServiceProvider。

    我们进一步看IRouteBuilder的实现RouterBuilder:

    public class RouteBuilder : IRouteBuilder
        {
            public RouteBuilder()
            {
                Routes = new List<IRouter>();
            }
    
            public IRouter DefaultHandler { get; set; }
    
            public IServiceProvider ServiceProvider { get; set; }
    
            public IList<IRouter> Routes
            {
                get;
                private set;
            }
    
            public IRouter Build()
            {
                var routeCollection = new RouteCollection();
    
                foreach (var route in Routes)
                {
                    routeCollection.Add(route);
                }
    
                return routeCollection;
            }
        }
    View Code

    主要的实现是Build方法,这个方法的实现也很简单,遍历Routes向一个实现了IRouter接口的RouteCollection对象添加IRouter,我们可以先看一下RouteCollection的实现:

    public class RouteCollection : IRouteCollection
        {
            private readonly List<IRouter> _routes = new List<IRouter>();
            private readonly List<IRouter> _unnamedRoutes = new List<IRouter>();
            private readonly Dictionary<string, INamedRouter> _namedRoutes =
                                        new Dictionary<string, INamedRouter>(StringComparer.OrdinalIgnoreCase);
    
            private RouteOptions _options;
    
            public IRouter this[int index]
            {
                get { return _routes[index]; }
            }
    
            public int Count
            {
                get { return _routes.Count; }
            }
    
            public void Add([NotNull] IRouter router)
            {
                var namedRouter = router as INamedRouter;
                if (namedRouter != null)
                {
                    if (!string.IsNullOrEmpty(namedRouter.Name))
                    {
                        _namedRoutes.Add(namedRouter.Name, namedRouter);
                    }
                }
                else
                {
                    _unnamedRoutes.Add(router);
                }
    
                _routes.Add(router);
            }
    
            public async virtual Task RouteAsync(RouteContext context)
            {
                for (var i = 0; i < Count; i++)
                {
                    var route = this[i];
    
                    var oldRouteData = context.RouteData;
    
                    var newRouteData = new RouteData(oldRouteData);
                    newRouteData.Routers.Add(route);
    
                    try
                    {
                        context.RouteData = newRouteData;
    
                        await route.RouteAsync(context);
                        if (context.IsHandled)
                        {
                            break;
                        }
                    }
                    finally
                    {
                        if (!context.IsHandled)
                        {
                            context.RouteData = oldRouteData;
                        }
                    }
                }
            }
    
            public virtual VirtualPathData GetVirtualPath(VirtualPathContext context)
            {
                EnsureOptions(context.Context);
    
                // If we're using Best-Effort link generation then it means that we'll first look for a route where
                // the route values are validated (context.IsBound == true). If we can't find a match like that, then
                // we'll return the path from the first route to return one.
                var useBestEffort = _options.UseBestEffortLinkGeneration;
    
                if (!string.IsNullOrEmpty(context.RouteName))
                {
                    var isValidated = false;
                    VirtualPathData bestPathData = null;
                    INamedRouter matchedNamedRoute;
                    if (_namedRoutes.TryGetValue(context.RouteName, out matchedNamedRoute))
                    {
                        bestPathData = matchedNamedRoute.GetVirtualPath(context);
                        isValidated = context.IsBound;
                    }
    
                    // If we get here and context.IsBound == true, then we know we have a match, we want to keep
                    // iterating to see if we have multiple matches.
                    foreach (var unnamedRoute in _unnamedRoutes)
                    {
                        // reset because we're sharing the context
                        context.IsBound = false;
    
                        var pathData = unnamedRoute.GetVirtualPath(context);
                        if (pathData == null)
                        {
                            continue;
                        }
    
                        if (bestPathData != null)
                        {
                            // There was already a previous route which matched the name.
                            throw new InvalidOperationException(
                                Resources.FormatNamedRoutes_AmbiguousRoutesFound(context.RouteName));
                        }
                        else if (context.IsBound)
                        {
                            // This is the first 'validated' match that we've found.
                            bestPathData = pathData;
                            isValidated = true;
                        }
                        else
                        {
                            Debug.Assert(bestPathData == null);
    
                            // This is the first 'unvalidated' match that we've found.
                            bestPathData = pathData;
                            isValidated = false;
                        }
                    }
    
                    if (isValidated || useBestEffort)
                    {
                        context.IsBound = isValidated;
    
                        if (bestPathData != null)
                        {
                            bestPathData = new VirtualPathData(
                                bestPathData.Router,
                                NormalizeVirtualPath(bestPathData.VirtualPath),
                                bestPathData.DataTokens);
                        }
    
                        return bestPathData;
                    }
                    else
                    {
                        return null;
                    }
                }
                else
                {
                    VirtualPathData bestPathData = null;
                    for (var i = 0; i < Count; i++)
                    {
                        var route = this[i];
    
                        var pathData = route.GetVirtualPath(context);
                        if (pathData == null)
                        {
                            continue;
                        }
    
                        if (context.IsBound)
                        {
                            // This route has validated route values, short circuit.
                            return new VirtualPathData(
                                pathData.Router,
                                NormalizeVirtualPath(pathData.VirtualPath),
                                pathData.DataTokens);
                        }
                        else if (bestPathData == null)
                        {
                            // The values aren't validated, but this is the best we've seen so far
                            bestPathData = pathData;
                        }
                    }
    
                    if (useBestEffort)
                    {
                        return new VirtualPathData(
                            bestPathData.Router,
                            NormalizeVirtualPath(bestPathData.VirtualPath),
                            bestPathData.DataTokens);
                    }
                    else
                    {
                        return null;
                    }
                }
            }
    
            private PathString NormalizeVirtualPath(PathString path)
            {
                var url = path.Value;
    
                if (!string.IsNullOrEmpty(url) && _options.LowercaseUrls)
                {
                    var indexOfSeparator = url.IndexOfAny(new char[] { '?', '#' });
    
                    // No query string, lowercase the url
                    if (indexOfSeparator == -1)
                    {
                        url = url.ToLowerInvariant();
                    }
                    else
                    {
                        var lowercaseUrl = url.Substring(0, indexOfSeparator).ToLowerInvariant();
                        var queryString = url.Substring(indexOfSeparator);
    
                        // queryString will contain the delimiter ? or # as the first character, so it's safe to append.
                        url = lowercaseUrl + queryString;
                    }
    
                    return new PathString(url);
                }
    
                return path;
            }
    
            private void EnsureOptions(HttpContext context)
            {
                if (_options == null)
                {
                    _options = context.RequestServices.GetRequiredService<IOptions<RouteOptions>>().Options;
                }
            }
        }

     乍一看这个类的功能还是比较庞大的,我们主要关注他对IRouter接口签名的实现:

    Task RouteAsync(RouteContext context):

    通过代码我们可以看到,这个方法主要对RouteCollection本身持有的Route 规则循环添加到路由上下文RouteContext.RouteData中。

    VirtualPathData GetVirtualPath(VirtualPathContext context):

    
    
  • 相关阅读:
    Attribute+Reflection,提高代码重用
    类型安全的EventHandlerList
    简单一招,使解决方案下的项目版本号统一
    T-SQL 随机返回特定行数据和分页查询
    2013年中国系统架构师大会随想
    C#实现在注册表中保存信息
    滤镜
    蒙版
    图层样式和混合模式
    布尔运算
  • 原文地址:https://www.cnblogs.com/LiJianBlog/p/4557033.html
Copyright © 2011-2022 走看看