zoukankan      html  css  js  c++  java
  • asp.net core 3.1 Routing(一)

    先看下IApplicationBuilder的扩展方法

    public static IApplicationBuilder UseRouter(this IApplicationBuilder builder, IRouter router)
            {
                if (builder == null)
                {
                    throw new ArgumentNullException(nameof(builder));
                }
    
                if (router == null)
                {
                    throw new ArgumentNullException(nameof(router));
                }
    
                if (builder.ApplicationServices.GetService(typeof(RoutingMarkerService)) == null)
                {
                    throw new InvalidOperationException(Resources.FormatUnableToFindServices(
                        nameof(IServiceCollection),
                        nameof(RoutingServiceCollectionExtensions.AddRouting),
                        "ConfigureServices(...)"));
                }
    
                return builder.UseMiddleware<RouterMiddleware>(router);
            }
    
            /// <summary>
            /// Adds a <see cref="RouterMiddleware"/> middleware to the specified <see cref="IApplicationBuilder"/>
            /// with the <see cref="IRouter"/> built from configured <see cref="IRouteBuilder"/>.
            /// </summary>
            /// <param name="builder">The <see cref="IApplicationBuilder"/> to add the middleware to.</param>
            /// <param name="action">An <see cref="Action{IRouteBuilder}"/> to configure the provided <see cref="IRouteBuilder"/>.</param>
            /// <returns>A reference to this instance after the operation has completed.</returns>
            public static IApplicationBuilder UseRouter(this IApplicationBuilder builder, Action<IRouteBuilder> action)
            {
                if (builder == null)
                {
                    throw new ArgumentNullException(nameof(builder));
                }
    
                if (action == null)
                {
                    throw new ArgumentNullException(nameof(action));
                }
    
                if (builder.ApplicationServices.GetService(typeof(RoutingMarkerService)) == null)
                {
                    throw new InvalidOperationException(Resources.FormatUnableToFindServices(
                        nameof(IServiceCollection),
                        nameof(RoutingServiceCollectionExtensions.AddRouting),
                        "ConfigureServices(...)"));
                }
    
                var routeBuilder = new RouteBuilder(builder);
                action(routeBuilder);
    
                return builder.UseRouter(routeBuilder.Build());
            }

    当调用UseRouter的时候,其实就注册引用了RouterMiddleware中间件

    public class RouterMiddleware
        {
            private readonly ILogger _logger;
            private readonly RequestDelegate _next;
            private readonly IRouter _router;
    
            public RouterMiddleware(
                RequestDelegate next,
                ILoggerFactory loggerFactory,
                IRouter router)
            {
                _next = next;
                _router = router;
    
                _logger = loggerFactory.CreateLogger<RouterMiddleware>();
            }
    
            public async Task Invoke(HttpContext httpContext)
            {
                var context = new RouteContext(httpContext);
                context.RouteData.Routers.Add(_router);
    
                await _router.RouteAsync(context);
    
                if (context.Handler == null)
                {
                    _logger.RequestNotMatched();
                    await _next.Invoke(httpContext);
                }
                else
                {
                    var routingFeature = new RoutingFeature()
                    {
                        RouteData = context.RouteData
                    };
    
                    // Set the RouteValues on the current request, this is to keep the IRouteValuesFeature inline with the IRoutingFeature
                    httpContext.Request.RouteValues = context.RouteData.Values;
                    httpContext.Features.Set<IRoutingFeature>(routingFeature);
    
                    await context.Handler(context.HttpContext);
                }
            }
        }

    RouterMiddleware中间件实现很简单,通过构造函数传进来一个IRouter对象

    调用IRouter的RouteAsync方法,判断Handler属性是否为null,如果是则调用下一个中间件,如果不为null,则调用Handler处理请求

    public interface IRouter
        {
            Task RouteAsync(RouteContext context);
    
            VirtualPathData GetVirtualPath(VirtualPathContext context);
        }

     RouteBase是一个抽象类

    public RouteBase(
                string template,
                string name,
                IInlineConstraintResolver constraintResolver,
                RouteValueDictionary defaults,
                IDictionary<string, object> constraints,
                RouteValueDictionary dataTokens)
            {
                if (constraintResolver == null)
                {
                    throw new ArgumentNullException(nameof(constraintResolver));
                }
    
                template = template ?? string.Empty;
                Name = name;
                ConstraintResolver = constraintResolver;
                DataTokens = dataTokens ?? new RouteValueDictionary();
    
                try
                {
                    // Data we parse from the template will be used to fill in the rest of the constraints or
                    // defaults. The parser will throw for invalid routes.
                    ParsedTemplate = TemplateParser.Parse(template);
    
                    Constraints = GetConstraints(constraintResolver, ParsedTemplate, constraints);
                    Defaults = GetDefaults(ParsedTemplate, defaults);
                }
                catch (Exception exception)
                {
                    throw new RouteCreationException(Resources.FormatTemplateRoute_Exception(name, template), exception);
                }
            }

    看下其构造函数

    先对分析传进来的路由模板,构建RouteTemplate对象,具体如何分析在RoutePatternParser类中实现

    再获取路由对应的路由约束

    最后设置路由的默认值

    public virtual Task RouteAsync(RouteContext context)
            {
                if (context == null)
                {
                    throw new ArgumentNullException(nameof(context));
                }
    
                EnsureMatcher();
                EnsureLoggers(context.HttpContext);
    
                var requestPath = context.HttpContext.Request.Path;
    
                if (!_matcher.TryMatch(requestPath, context.RouteData.Values))
                {
                    // If we got back a null value set, that means the URI did not match
                    return Task.CompletedTask;
                }
    
                // Perf: Avoid accessing dictionaries if you don't need to write to them, these dictionaries are all
                // created lazily.
                if (DataTokens.Count > 0)
                {
                    MergeValues(context.RouteData.DataTokens, DataTokens);
                }
    
                if (!RouteConstraintMatcher.Match(
                    Constraints,
                    context.RouteData.Values,
                    context.HttpContext,
                    this,
                    RouteDirection.IncomingRequest,
                    _constraintLogger))
                {
                    return Task.CompletedTask;
                }
                _logger.RequestMatchedRoute(Name, ParsedTemplate.TemplateText);
    
                return OnRouteMatched(context);
            }

    RouterBase的RouteAsync方法中,先调用TemplateMatcher类的TryMatch方法判断路由是否匹配

    再判断路由约束是否通过

    以上条件都通过后,再调用其抽象方法OnRouteMatched

    再看下Route类,其构造函数接收IRouter的参数,当调用OnRouteMatched方法时,其实是调用了参数IRouter的RouteAsync方法

    再看下RouteBuilder类

    public class RouteBuilder : IRouteBuilder
        {
            public RouteBuilder(IApplicationBuilder applicationBuilder)
                : this(applicationBuilder, defaultHandler: null)
            {
            }
    
            public RouteBuilder(IApplicationBuilder applicationBuilder, IRouter defaultHandler)
            {
                if (applicationBuilder == null)
                {
                    throw new ArgumentNullException(nameof(applicationBuilder));
                }
    
                if (applicationBuilder.ApplicationServices.GetService(typeof(RoutingMarkerService)) == null)
                {
                    throw new InvalidOperationException(Resources.FormatUnableToFindServices(
                        nameof(IServiceCollection),
                        nameof(RoutingServiceCollectionExtensions.AddRouting),
                        "ConfigureServices(...)"));
                }
    
                ApplicationBuilder = applicationBuilder;
                DefaultHandler = defaultHandler;
                ServiceProvider = applicationBuilder.ApplicationServices;
    
                Routes = new List<IRouter>();
            }
    
            public IApplicationBuilder ApplicationBuilder { get; }
    
            public IRouter DefaultHandler { get; set; }
    
            public IServiceProvider ServiceProvider { get; }
    
            public IList<IRouter> Routes { get; }
    
            public IRouter Build()
            {
                var routeCollection = new RouteCollection();
    
                foreach (var route in Routes)
                {
                    routeCollection.Add(route);
                }
    
                return routeCollection;
            }
        }

    RouteBuilder中有Routes集合,我们可以添加自己的路由

    Build()方法创建RouteCollection集合,把Routes中的路由添加到RouteCollection中

    public class RouteCollection : IRouteCollection
        {
            private readonly static char[] UrlQueryDelimiters = new char[] { '?', '#' };
            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(IRouter router)
            {
                if (router == null)
                {
                    throw new ArgumentNullException(nameof(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)
            {
                // Perf: We want to avoid allocating a new RouteData for each route we need to process.
                // We can do this by snapshotting the state at the beginning and then restoring it
                // for each router we execute.
                var snapshot = context.RouteData.PushState(null, values: null, dataTokens: null);
    
                for (var i = 0; i < Count; i++)
                {
                    var route = this[i];
                    context.RouteData.Routers.Add(route);
    
                    try
                    {
                        await route.RouteAsync(context);
    
                        if (context.Handler != null)
                        {
                            break;
                        }
                    }
                    finally
                    {
                        if (context.Handler == null)
                        {
                            snapshot.Restore();
                        }
                    }
                }
            }
    
            public virtual VirtualPathData GetVirtualPath(VirtualPathContext context)
            {
                EnsureOptions(context.HttpContext);
    
                if (!string.IsNullOrEmpty(context.RouteName))
                {
                    VirtualPathData namedRoutePathData = null;
    
                    if (_namedRoutes.TryGetValue(context.RouteName, out var matchedNamedRoute))
                    {
                        namedRoutePathData = matchedNamedRoute.GetVirtualPath(context);
                    }
    
                    var pathData = GetVirtualPath(context, _unnamedRoutes);
    
                    // If the named route and one of the unnamed routes also matches, then we have an ambiguity.
                    if (namedRoutePathData != null && pathData != null)
                    {
                        var message = Resources.FormatNamedRoutes_AmbiguousRoutesFound(context.RouteName);
                        throw new InvalidOperationException(message);
                    }
    
                    return NormalizeVirtualPath(namedRoutePathData ?? pathData);
                }
                else
                {
                    return NormalizeVirtualPath(GetVirtualPath(context, _routes));
                }
            }
    
            private VirtualPathData GetVirtualPath(VirtualPathContext context, List<IRouter> routes)
            {
                for (var i = 0; i < routes.Count; i++)
                {
                    var route = routes[i];
    
                    var pathData = route.GetVirtualPath(context);
                    if (pathData != null)
                    {
                        return pathData;
                    }
                }
    
                return null;
            }
    
            private VirtualPathData NormalizeVirtualPath(VirtualPathData pathData)
            {
                if (pathData == null)
                {
                    return pathData;
                }
    
                var url = pathData.VirtualPath;
    
                if (!string.IsNullOrEmpty(url) && (_options.LowercaseUrls || _options.AppendTrailingSlash))
                {
                    var indexOfSeparator = url.IndexOfAny(UrlQueryDelimiters);
                    var urlWithoutQueryString = url;
                    var queryString = string.Empty;
    
                    if (indexOfSeparator != -1)
                    {
                        urlWithoutQueryString = url.Substring(0, indexOfSeparator);
                        queryString = url.Substring(indexOfSeparator);
                    }
    
                    if (_options.LowercaseUrls)
                    {
                        urlWithoutQueryString = urlWithoutQueryString.ToLowerInvariant();
                    }
    
                    if (_options.LowercaseUrls && _options.LowercaseQueryStrings)
                    {
                        queryString = queryString.ToLowerInvariant();
                    }
    
                    if (_options.AppendTrailingSlash && !urlWithoutQueryString.EndsWith("/", StringComparison.Ordinal))
                    {
                        urlWithoutQueryString += "/";
                    }
    
                    // queryString will contain the delimiter ? or # as the first character, so it's safe to append.
                    url = urlWithoutQueryString + queryString;
    
                    return new VirtualPathData(pathData.Router, url, pathData.DataTokens);
                }
    
                return pathData;
            }
    
            private void EnsureOptions(HttpContext context)
            {
                if (_options == null)
                {
                    _options = context.RequestServices.GetRequiredService<IOptions<RouteOptions>>().Value;
                }
            }
        }

    RouteCollection也实现了IRouter接口

    在执行RouteAsync方法时,其会遍历内部的各个路由,并实现每个路由的RouteAsync方法,再判断RouteContext的Handler属性,如果Handler属性不为null,则说明找到对应的路由处理该请求了

  • 相关阅读:
    php计算utf8字符串长度
    php和js字符串的acsii码函数
    快速排序的php实现
    bzoj 2822 [AHOI2012]树屋阶梯 卡特兰数
    bzoj 1485 [HNOI2009]有趣的数列 卡特兰数
    bzoj 4173 打表???
    bzoj [Noi2002]Savage 扩展欧几里得
    bzoj 3505 [Cqoi2014]数三角形 组合
    bzoj 2820 莫比乌斯反演
    Travel 并查集
  • 原文地址:https://www.cnblogs.com/lanpingwang/p/12641182.html
Copyright © 2011-2022 走看看