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

    EndpointRoute新的路由方案

    public static class EndpointRoutingApplicationBuilderExtensions
        {
            private const string EndpointRouteBuilder = "__EndpointRouteBuilder";
    
            /// <summary>
            /// Adds a <see cref="EndpointRoutingMiddleware"/> middleware to the specified <see cref="IApplicationBuilder"/>.
            /// </summary>
            /// <param name="builder">The <see cref="IApplicationBuilder"/> to add the middleware to.</param>
            /// <returns>A reference to this instance after the operation has completed.</returns>
            /// <remarks>
            /// <para>
            /// A call to <see cref="UseRouting(IApplicationBuilder)"/> must be followed by a call to
            /// <see cref="UseEndpoints(IApplicationBuilder, Action{IEndpointRouteBuilder})"/> for the same <see cref="IApplicationBuilder"/>
            /// instance.
            /// </para>
            /// <para>
            /// The <see cref="EndpointRoutingMiddleware"/> defines a point in the middleware pipeline where routing decisions are
            /// made, and an <see cref="Endpoint"/> is associated with the <see cref="HttpContext"/>. The <see cref="EndpointMiddleware"/>
            /// defines a point in the middleware pipeline where the current <see cref="Endpoint"/> is executed. Middleware between
            /// the <see cref="EndpointRoutingMiddleware"/> and <see cref="EndpointMiddleware"/> may observe or change the
            /// <see cref="Endpoint"/> associated with the <see cref="HttpContext"/>.
            /// </para>
            /// </remarks>
            public static IApplicationBuilder UseRouting(this IApplicationBuilder builder)
            {
                if (builder == null)
                {
                    throw new ArgumentNullException(nameof(builder));
                }
    
                VerifyRoutingServicesAreRegistered(builder);
    
                var endpointRouteBuilder = new DefaultEndpointRouteBuilder(builder);
                builder.Properties[EndpointRouteBuilder] = endpointRouteBuilder;
    
                return builder.UseMiddleware<EndpointRoutingMiddleware>(endpointRouteBuilder);
            }
    
            /// <summary>
            /// Adds a <see cref="EndpointMiddleware"/> middleware to the specified <see cref="IApplicationBuilder"/>
            /// with the <see cref="EndpointDataSource"/> instances built from configured <see cref="IEndpointRouteBuilder"/>.
            /// The <see cref="EndpointMiddleware"/> will execute the <see cref="Endpoint"/> associated with the current
            /// request.
            /// </summary>
            /// <param name="builder">The <see cref="IApplicationBuilder"/> to add the middleware to.</param>
            /// <param name="configure">An <see cref="Action{IEndpointRouteBuilder}"/> to configure the provided <see cref="IEndpointRouteBuilder"/>.</param>
            /// <returns>A reference to this instance after the operation has completed.</returns>
            /// <remarks>
            /// <para>
            /// A call to <see cref="UseEndpoints(IApplicationBuilder, Action{IEndpointRouteBuilder})"/> must be preceded by a call to
            /// <see cref="UseRouting(IApplicationBuilder)"/> for the same <see cref="IApplicationBuilder"/>
            /// instance.
            /// </para>
            /// <para>
            /// The <see cref="EndpointRoutingMiddleware"/> defines a point in the middleware pipeline where routing decisions are
            /// made, and an <see cref="Endpoint"/> is associated with the <see cref="HttpContext"/>. The <see cref="EndpointMiddleware"/>
            /// defines a point in the middleware pipeline where the current <see cref="Endpoint"/> is executed. Middleware between
            /// the <see cref="EndpointRoutingMiddleware"/> and <see cref="EndpointMiddleware"/> may observe or change the
            /// <see cref="Endpoint"/> associated with the <see cref="HttpContext"/>.
            /// </para>
            /// </remarks>
            public static IApplicationBuilder UseEndpoints(this IApplicationBuilder builder, Action<IEndpointRouteBuilder> configure)
            {
                if (builder == null)
                {
                    throw new ArgumentNullException(nameof(builder));
                }
    
                if (configure == null)
                {
                    throw new ArgumentNullException(nameof(configure));
                }
    
                VerifyRoutingServicesAreRegistered(builder);
    
                VerifyEndpointRoutingMiddlewareIsRegistered(builder, out var endpointRouteBuilder);
    
                configure(endpointRouteBuilder);
    
                // Yes, this mutates an IOptions. We're registering data sources in a global collection which
                // can be used for discovery of endpoints or URL generation.
                //
                // Each middleware gets its own collection of data sources, and all of those data sources also
                // get added to a global collection.
                var routeOptions = builder.ApplicationServices.GetRequiredService<IOptions<RouteOptions>>();
                foreach (var dataSource in endpointRouteBuilder.DataSources)
                {
                    routeOptions.Value.EndpointDataSources.Add(dataSource);
                }
    
                return builder.UseMiddleware<EndpointMiddleware>();
            }
    
            private static void VerifyRoutingServicesAreRegistered(IApplicationBuilder app)
            {
                // Verify if AddRouting was done before calling UseEndpointRouting/UseEndpoint
                // We use the RoutingMarkerService to make sure if all the services were added.
                if (app.ApplicationServices.GetService(typeof(RoutingMarkerService)) == null)
                {
                    throw new InvalidOperationException(Resources.FormatUnableToFindServices(
                        nameof(IServiceCollection),
                        nameof(RoutingServiceCollectionExtensions.AddRouting),
                        "ConfigureServices(...)"));
                }
            }
    
            private static void VerifyEndpointRoutingMiddlewareIsRegistered(IApplicationBuilder app, out DefaultEndpointRouteBuilder endpointRouteBuilder)
            {
                if (!app.Properties.TryGetValue(EndpointRouteBuilder, out var obj))
                {
                    var message =
                        $"{nameof(EndpointRoutingMiddleware)} matches endpoints setup by {nameof(EndpointMiddleware)} and so must be added to the request " +
                        $"execution pipeline before {nameof(EndpointMiddleware)}. " +
                        $"Please add {nameof(EndpointRoutingMiddleware)} by calling '{nameof(IApplicationBuilder)}.{nameof(UseRouting)}' inside the call " +
                        $"to 'Configure(...)' in the application startup code.";
                    throw new InvalidOperationException(message);
                }
    
                // If someone messes with this, just let it crash.
                endpointRouteBuilder = (DefaultEndpointRouteBuilder)obj;
    
                // This check handles the case where Map or something else that forks the pipeline is called between the two
                // routing middleware.
                if (!object.ReferenceEquals(app, endpointRouteBuilder.ApplicationBuilder))
                {
                    var message =
                        $"The {nameof(EndpointRoutingMiddleware)} and {nameof(EndpointMiddleware)} must be added to the same {nameof(IApplicationBuilder)} instance. " +
                        $"To use Endpoint Routing with 'Map(...)', make sure to call '{nameof(IApplicationBuilder)}.{nameof(UseRouting)}' before " +
                        $"'{nameof(IApplicationBuilder)}.{nameof(UseEndpoints)}' for each branch of the middleware pipeline.";
                    throw new InvalidOperationException(message);
                }
            }
        }

    这里涉及到两个中间件EndpointRoutingMiddleware和EndpointMiddleware

    internal sealed class EndpointRoutingMiddleware
        {
            private const string DiagnosticsEndpointMatchedKey = "Microsoft.AspNetCore.Routing.EndpointMatched";
    
            private readonly MatcherFactory _matcherFactory;
            private readonly ILogger _logger;
            private readonly EndpointDataSource _endpointDataSource;
            private readonly DiagnosticListener _diagnosticListener;
            private readonly RequestDelegate _next;
    
            private Task<Matcher> _initializationTask;
    
            public EndpointRoutingMiddleware(
                MatcherFactory matcherFactory,
                ILogger<EndpointRoutingMiddleware> logger,
                IEndpointRouteBuilder endpointRouteBuilder,
                DiagnosticListener diagnosticListener,
                RequestDelegate next)
            {
                if (endpointRouteBuilder == null)
                {
                    throw new ArgumentNullException(nameof(endpointRouteBuilder));
                }
    
                _matcherFactory = matcherFactory ?? throw new ArgumentNullException(nameof(matcherFactory));
                _logger = logger ?? throw new ArgumentNullException(nameof(logger));
                _diagnosticListener = diagnosticListener ?? throw new ArgumentNullException(nameof(diagnosticListener));
                _next = next ?? throw new ArgumentNullException(nameof(next));
    
                _endpointDataSource = new CompositeEndpointDataSource(endpointRouteBuilder.DataSources);
            }
    
            public Task Invoke(HttpContext httpContext)
            {
                // There's already an endpoint, skip maching completely
                var endpoint = httpContext.GetEndpoint();
                if (endpoint != null)
                {
                    Log.MatchSkipped(_logger, endpoint);
                    return _next(httpContext);
                }
    
                // There's an inherent race condition between waiting for init and accessing the matcher
                // this is OK because once `_matcher` is initialized, it will not be set to null again.
                var matcherTask = InitializeAsync();
                if (!matcherTask.IsCompletedSuccessfully)
                {
                    return AwaitMatcher(this, httpContext, matcherTask);
                }
    
                var matchTask = matcherTask.Result.MatchAsync(httpContext);
                if (!matchTask.IsCompletedSuccessfully)
                {
                    return AwaitMatch(this, httpContext, matchTask);
                }
    
                return SetRoutingAndContinue(httpContext);
    
                // Awaited fallbacks for when the Tasks do not synchronously complete
                static async Task AwaitMatcher(EndpointRoutingMiddleware middleware, HttpContext httpContext, Task<Matcher> matcherTask)
                {
                    var matcher = await matcherTask;
                    await matcher.MatchAsync(httpContext);
                    await middleware.SetRoutingAndContinue(httpContext);
                }
    
                static async Task AwaitMatch(EndpointRoutingMiddleware middleware, HttpContext httpContext, Task matchTask)
                {
                    await matchTask;
                    await middleware.SetRoutingAndContinue(httpContext);
                }
    
            }
    
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            private Task SetRoutingAndContinue(HttpContext httpContext)
            {
                // If there was no mutation of the endpoint then log failure
                var endpoint = httpContext.GetEndpoint();
                if (endpoint == null)
                {
                    Log.MatchFailure(_logger);
                }
                else
                {
                    // Raise an event if the route matched
                    if (_diagnosticListener.IsEnabled() && _diagnosticListener.IsEnabled(DiagnosticsEndpointMatchedKey))
                    {
                        // We're just going to send the HttpContext since it has all of the relevant information
                        _diagnosticListener.Write(DiagnosticsEndpointMatchedKey, httpContext);
                    }
    
                    Log.MatchSuccess(_logger, endpoint);
                }
    
                return _next(httpContext);
            }
    
            // Initialization is async to avoid blocking threads while reflection and things
            // of that nature take place.
            //
            // We've seen cases where startup is very slow if we  allow multiple threads to race
            // while initializing the set of endpoints/routes. Doing CPU intensive work is a
            // blocking operation if you have a low core count and enough work to do.
            private Task<Matcher> InitializeAsync()
            {
                var initializationTask = _initializationTask;
                if (initializationTask != null)
                {
                    return initializationTask;
                }
    
                return InitializeCoreAsync();
            }
    
            private Task<Matcher> InitializeCoreAsync()
            {
                var initialization = new TaskCompletionSource<Matcher>(TaskCreationOptions.RunContinuationsAsynchronously);
                var initializationTask = Interlocked.CompareExchange(ref _initializationTask, initialization.Task, null);
                if (initializationTask != null)
                {
                    // This thread lost the race, join the existing task.
                    return initializationTask;
                }
    
                // This thread won the race, do the initialization.
                try
                {
                    var matcher = _matcherFactory.CreateMatcher(_endpointDataSource);
    
                    // Now replace the initialization task with one created with the default execution context.
                    // This is important because capturing the execution context will leak memory in ASP.NET Core.
                    using (ExecutionContext.SuppressFlow())
                    {
                        _initializationTask = Task.FromResult(matcher);
                    }
    
                    // Complete the task, this will unblock any requests that came in while initializing.
                    initialization.SetResult(matcher);
                    return initialization.Task;
                }
                catch (Exception ex)
                {
                    // Allow initialization to occur again. Since DataSources can change, it's possible
                    // for the developer to correct the data causing the failure.
                    _initializationTask = null;
    
                    // Complete the task, this will throw for any requests that came in while initializing.
                    initialization.SetException(ex);
                    return initialization.Task;
                }
            }
    
            private static class Log
            {
                private static readonly Action<ILogger, string, Exception> _matchSuccess = LoggerMessage.Define<string>(
                    LogLevel.Debug,
                    new EventId(1, "MatchSuccess"),
                    "Request matched endpoint '{EndpointName}'");
    
                private static readonly Action<ILogger, Exception> _matchFailure = LoggerMessage.Define(
                    LogLevel.Debug,
                    new EventId(2, "MatchFailure"),
                    "Request did not match any endpoints");
    
                private static readonly Action<ILogger, string, Exception> _matchingSkipped = LoggerMessage.Define<string>(
                    LogLevel.Debug,
                    new EventId(3, "MatchingSkipped"),
                    "Endpoint '{EndpointName}' already set, skipping route matching.");
    
                public static void MatchSuccess(ILogger logger, Endpoint endpoint)
                {
                    _matchSuccess(logger, endpoint.DisplayName, null);
                }
    
                public static void MatchFailure(ILogger logger)
                {
                    _matchFailure(logger, null);
                }
    
                public static void MatchSkipped(ILogger logger, Endpoint endpoint)
                {
                    _matchingSkipped(logger, endpoint.DisplayName, null);
                }
            }
        }

    EndpointRoutingMiddleware先从HttpContext中获取Endpoint,如果不为null,则继续调用下一个中间件

    如果找不到,则调用DfaMatcher类的MatchAsync去找到对应的Endpoint,并设置HttpContext

    具体如何查找到对应的Endpoint,这里不细说了

    看下Endpoint

    public class Endpoint
        {
            /// <summary>
            /// Creates a new instance of <see cref="Endpoint"/>.
            /// </summary>
            /// <param name="requestDelegate">The delegate used to process requests for the endpoint.</param>
            /// <param name="metadata">
            /// The endpoint <see cref="EndpointMetadataCollection"/>. May be null.
            /// </param>
            /// <param name="displayName">
            /// The informational display name of the endpoint. May be null.
            /// </param>
            public Endpoint(
                RequestDelegate requestDelegate,
                EndpointMetadataCollection metadata,
                string displayName)
            {
                // All are allowed to be null
                RequestDelegate = requestDelegate;
                Metadata = metadata ?? EndpointMetadataCollection.Empty;
                DisplayName = displayName;
            }
    
            /// <summary>
            /// Gets the informational display name of this endpoint.
            /// </summary>
            public string DisplayName { get; }
    
            /// <summary>
            /// Gets the collection of metadata associated with this endpoint.
            /// </summary>
            public EndpointMetadataCollection Metadata { get; }
    
            /// <summary>
            /// Gets the delegate used to process requests for the endpoint.
            /// </summary>
            public RequestDelegate RequestDelegate { get; }
    
            public override string ToString() => DisplayName ?? base.ToString();
        }

    其中RequestDelegate是处理真个http请求

    EndpointRoutingMiddleware中间件的主要作用就是设置HttpContext的IEndpointFeature属性

    internal sealed class EndpointMiddleware
        {
            internal const string AuthorizationMiddlewareInvokedKey = "__AuthorizationMiddlewareWithEndpointInvoked";
            internal const string CorsMiddlewareInvokedKey = "__CorsMiddlewareWithEndpointInvoked";
    
            private readonly ILogger _logger;
            private readonly RequestDelegate _next;
            private readonly RouteOptions _routeOptions;
    
            public EndpointMiddleware(
                ILogger<EndpointMiddleware> logger,
                RequestDelegate next,
                IOptions<RouteOptions> routeOptions)
            {
                _logger = logger ?? throw new ArgumentNullException(nameof(logger));
                _next = next ?? throw new ArgumentNullException(nameof(next));
                _routeOptions = routeOptions?.Value ?? throw new ArgumentNullException(nameof(routeOptions));
            }
    
            public Task Invoke(HttpContext httpContext)
            {
                var endpoint = httpContext.GetEndpoint();
                if (endpoint?.RequestDelegate != null)
                {
                    if (!_routeOptions.SuppressCheckForUnhandledSecurityMetadata)
                    {
                        if (endpoint.Metadata.GetMetadata<IAuthorizeData>() != null &&
                            !httpContext.Items.ContainsKey(AuthorizationMiddlewareInvokedKey))
                        {
                            ThrowMissingAuthMiddlewareException(endpoint);
                        }
    
                        if (endpoint.Metadata.GetMetadata<ICorsMetadata>() != null &&
                            !httpContext.Items.ContainsKey(CorsMiddlewareInvokedKey))
                        {
                            ThrowMissingCorsMiddlewareException(endpoint);
                        }
                    }
    
                    Log.ExecutingEndpoint(_logger, endpoint);
    
                    try
                    {
                        var requestTask = endpoint.RequestDelegate(httpContext);
                        if (!requestTask.IsCompletedSuccessfully)
                        {
                            return AwaitRequestTask(endpoint, requestTask, _logger);
                        }
                    }
                    catch (Exception exception)
                    {
                        Log.ExecutedEndpoint(_logger, endpoint);
                        return Task.FromException(exception);
                    }
    
                    Log.ExecutedEndpoint(_logger, endpoint);
                    return Task.CompletedTask;
                }
    
                return _next(httpContext);
    
                static async Task AwaitRequestTask(Endpoint endpoint, Task requestTask, ILogger logger)
                {
                    try
                    {
                        await requestTask;
                    }
                    finally
                    {
                        Log.ExecutedEndpoint(logger, endpoint);
                    }
                }
            }
    
            private static void ThrowMissingAuthMiddlewareException(Endpoint endpoint)
            {
                throw new InvalidOperationException($"Endpoint {endpoint.DisplayName} contains authorization metadata, " +
                    "but a middleware was not found that supports authorization." +
                    Environment.NewLine +
                    "Configure your application startup by adding app.UseAuthorization() inside the call to Configure(..) in the application startup code. The call to app.UseAuthorization() must appear between app.UseRouting() and app.UseEndpoints(...).");
            }
    
            private static void ThrowMissingCorsMiddlewareException(Endpoint endpoint)
            {
                throw new InvalidOperationException($"Endpoint {endpoint.DisplayName} contains CORS metadata, " +
                    "but a middleware was not found that supports CORS." +
                    Environment.NewLine +
                    "Configure your application startup by adding app.UseCors() inside the call to Configure(..) in the application startup code. The call to app.UseAuthorization() must appear between app.UseRouting() and app.UseEndpoints(...).");
            }
    
            private static class Log
            {
                private static readonly Action<ILogger, string, Exception> _executingEndpoint = LoggerMessage.Define<string>(
                    LogLevel.Information,
                    new EventId(0, "ExecutingEndpoint"),
                    "Executing endpoint '{EndpointName}'");
    
                private static readonly Action<ILogger, string, Exception> _executedEndpoint = LoggerMessage.Define<string>(
                    LogLevel.Information,
                    new EventId(1, "ExecutedEndpoint"),
                    "Executed endpoint '{EndpointName}'");
    
                public static void ExecutingEndpoint(ILogger logger, Endpoint endpoint)
                {
                    _executingEndpoint(logger, endpoint.DisplayName, null);
                }
    
                public static void ExecutedEndpoint(ILogger logger, Endpoint endpoint)
                {
                    _executedEndpoint(logger, endpoint.DisplayName, null);
                }
            }
        }

    EndpointMiddleware中间件是获取HttpContext的EndPoint,并调用Endpoint的RequestDelegate处理请求

  • 相关阅读:
    BestCoder17 1001.Chessboard(hdu 5100) 解题报告
    codeforces 485A.Factory 解题报告
    codeforces 485B Valuable Resources 解题报告
    BestCoder16 1002.Revenge of LIS II(hdu 5087) 解题报告
    codeforces 374A Inna and Pink Pony 解题报告
    codeforces 483B Friends and Presents 解题报告
    BestCoder15 1002.Instruction(hdu 5083) 解题报告
    codeforces 483C.Diverse Permutation 解题报告
    codeforces 483A. Counterexample 解题报告
    NSArray中地内存管理 理解
  • 原文地址:https://www.cnblogs.com/lanpingwang/p/12641266.html
Copyright © 2011-2022 走看看