AspNetCore 路由 <3.0>

概览

路由系统将URL和终结点Endpoint之间进行映射。首先在构建系统时,需要生成所有的映射;在请求来的时候,找到对应的终结点;然后执行对应的终结点,这样路由就由3部分构成:

  • 终结点模型

  • 选择终结点中间件

  • 执行终结点中间件

为什么将选择和执行分为两个中间件,因为在选择好之后可以执行其他中间件,执行终结点中间件必须是最后一个,因为它不会继续调用其他中间件。

终结点模型

概览

image-20220407222128792

  • 通过RoutePatternFactory这个静态类将字符串解析成RoutePattern

  • 通过RouteEndPointBuilder将RoutePattern和RequestHandler封装,同时将RequestHandler的特性封装metadata.在内部解析成Endpoint

  • 将RouteEndPointBuilder封装成DefaultEndpointConventionBuilder添加到MedelEndpointDataSource中

Endpoint

终结点其实就是RequestDelegate,也就是最终执行的方法。metadata其实就是保存着方法的特性,但是对类型并没有做任何的限制

  public class Endpoint
  {
    public Endpoint(
      RequestDelegate requestDelegate,
      EndpointMetadataCollection? metadata,
      string? displayName)
    {
      this.RequestDelegate = requestDelegate;
      this.Metadata = metadata ?? EndpointMetadataCollection.Empty;
      this.DisplayName = displayName;
    }

    public string? DisplayName { get; }

    public EndpointMetadataCollection Metadata { get; }

    public RequestDelegate RequestDelegate { get; }

    public override string? ToString() => this.DisplayName ?? base.ToString();
  }

EndpointMetadataCollection

  public sealed class EndpointMetadataCollection : 
    IReadOnlyList<object>,
    IEnumerable<object>,
    IEnumerable,
    IReadOnlyCollection<object>
  {
    public static readonly EndpointMetadataCollection Empty = new EndpointMetadataCollection(Array.Empty<object>());
    private readonly object[] _items;
    private readonly ConcurrentDictionary<Type, object[]> _cache;

    public EndpointMetadataCollection(IEnumerable<object> items)
    {
      this._items = items != null ? items.ToArray<object>() : throw new ArgumentNullException(nameof (items));
      this._cache = new ConcurrentDictionary<Type, object[]>();
    }

    public EndpointMetadataCollection(params object[] items)
      : this((IEnumerable<object>) items)
    {
    }

    public object this[int index] => this._items[index];

    public int Count => this._items.Length;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T? GetMetadata<T>() where T : class
    {
      object[] objArray1;
      if (!this._cache.TryGetValue(typeof (T), out objArray1))
        return this.GetMetadataSlow<T>();
      T[] objArray2 = (T[]) objArray1;
      int length = objArray2.Length;
      return length <= 0 ? default (T) : objArray2[length - 1];
    }

    private T? GetMetadataSlow<T>() where T : class
    {
      T[] orderedMetadataSlow = this.GetOrderedMetadataSlow<T>();
      int length = orderedMetadataSlow.Length;
      return length <= 0 ? default (T) : orderedMetadataSlow[length - 1];
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public IReadOnlyList<T> GetOrderedMetadata<T>() where T : class
    {
      object[] objArray;
      return this._cache.TryGetValue(typeof (T), out objArray) ? (IReadOnlyList<T>) objArray : (IReadOnlyList<T>) this.GetOrderedMetadataSlow<T>();
    }

    private T[] GetOrderedMetadataSlow<T>() where T : class
    {
      List<T> objList = (List<T>) null;
      foreach (object obj1 in this._items)
      {
        if (obj1 is T obj2)
        {
          if (objList == null)
            objList = new List<T>();
          objList.Add(obj2);
        }
      }
      T[] orderedMetadataSlow = objList == null ? Array.Empty<T>() : objList.ToArray();
      this._cache.TryAdd(typeof (T), (object[]) orderedMetadataSlow);
      return orderedMetadataSlow;
    }

    public EndpointMetadataCollection.Enumerator GetEnumerator() => new EndpointMetadataCollection.Enumerator(this);

    IEnumerator<object> IEnumerable<object>.GetEnumerator() => (IEnumerator<object>) this.GetEnumerator();

    IEnumerator IEnumerable.GetEnumerator() => (IEnumerator) this.GetEnumerator();

    public struct Enumerator : IEnumerator<object?>, IEnumerator, IDisposable
    {
      private object[] _items;
      private int _index;

      internal Enumerator(EndpointMetadataCollection collection)
      {
        this._items = collection._items;
        this._index = 0;
        this.Current = (object) null;
      }

      public object? Current { get; private set; }

      public void Dispose()
      {
      }

      public bool MoveNext()
      {
        if (this._index < this._items.Length)
        {
          this.Current = this._items[this._index++];
          return true;
        }
        this.Current = (object) null;
        return false;
      }

      public void Reset()
      {
        this._index = 0;
        this.Current = (object) null;
      }
    }
  }

RouteEndpoint

路由终结点就是增加了RoutePattern,URL的抽象

  public sealed class RouteEndpoint : Endpoint
  {
    public RouteEndpoint(
      RequestDelegate requestDelegate,
      RoutePattern routePattern,
      int order,
      EndpointMetadataCollection? metadata,
      string? displayName)
      : base(requestDelegate, metadata, displayName)
    {
      if (requestDelegate == null)
        throw new ArgumentNullException(nameof (requestDelegate));
      this.RoutePattern = routePattern != null ? routePattern : throw new ArgumentNullException(nameof (routePattern));
      this.Order = order;
    }

    public int Order { get; }

    public RoutePattern RoutePattern { get; }
  }

RoutePattern

URL的抽象,RoutePatternPathSegment就是将URL以/为分割符进行切分段,每一段可能是字符可能是参数,也可能是其他分隔符

  [DebuggerDisplay("{DebuggerToString()}")]
  public sealed class RoutePattern
  {
    public static readonly object RequiredValueAny = (object) new RoutePattern.RequiredValueAnySentinal();
    private const string SeparatorString = "/";

    internal static bool IsRequiredValueAny(object? value) => RoutePattern.RequiredValueAny == value;

    internal RoutePattern(
      string? rawText,
      IReadOnlyDictionary<string, object?> defaults,
      IReadOnlyDictionary<string, IReadOnlyList<RoutePatternParameterPolicyReference>> parameterPolicies,
      IReadOnlyDictionary<string, object?> requiredValues,
      IReadOnlyList<RoutePatternParameterPart> parameters,
      IReadOnlyList<RoutePatternPathSegment> pathSegments)
    {
      this.RawText = rawText;
      this.Defaults = defaults;
      this.ParameterPolicies = parameterPolicies;
      this.RequiredValues = requiredValues;
      this.Parameters = parameters;
      this.PathSegments = pathSegments;
      this.InboundPrecedence = RoutePrecedence.ComputeInbound(this);
      this.OutboundPrecedence = RoutePrecedence.ComputeOutbound(this);
    }

    public IReadOnlyDictionary<string, object?> Defaults { get; }

    public IReadOnlyDictionary<string, IReadOnlyList<RoutePatternParameterPolicyReference>> ParameterPolicies { get; }

    public IReadOnlyDictionary<string, object?> RequiredValues { get; }

    public Decimal InboundPrecedence { get; }

    public Decimal OutboundPrecedence { get; }

    public string? RawText { get; }

    public IReadOnlyList<RoutePatternParameterPart> Parameters { get; }

    public IReadOnlyList<RoutePatternPathSegment> PathSegments { get; }

    public RoutePatternParameterPart? GetParameter(string name)
    {
      if (name == null)
        throw new ArgumentNullException(nameof (name));
      IReadOnlyList<RoutePatternParameterPart> parameters = this.Parameters;
      int count = parameters.Count;
      for (int index = 0; index < count; ++index)
      {
        RoutePatternParameterPart parameter = parameters[index];
        if (string.Equals(parameter.Name, name, StringComparison.OrdinalIgnoreCase))
          return parameter;
      }
      return (RoutePatternParameterPart) null;
    }

    internal string DebuggerToString() => this.RawText ?? string.Join("/", this.PathSegments.Select<RoutePatternPathSegment, string>((Func<RoutePatternPathSegment, string>) (s => s.DebuggerToString())));

    [DebuggerDisplay("{DebuggerToString(),nq}")]
    private class RequiredValueAnySentinal
    {
      private string DebuggerToString() => "*any*";
    }
  }

RoutePatternPathSegment

  [DebuggerDisplay("{DebuggerToString()}")]
  public sealed class RoutePatternPathSegment
  {
    internal RoutePatternPathSegment(IReadOnlyList<RoutePatternPart> parts) => this.Parts = parts;

    public bool IsSimple => this.Parts.Count == 1;

    public IReadOnlyList<RoutePatternPart> Parts { get; }

    internal string DebuggerToString() => RoutePatternPathSegment.DebuggerToString(this.Parts);

    internal static string DebuggerToString(IReadOnlyList<RoutePatternPart> parts) => string.Join(string.Empty, parts.Select<RoutePatternPart, string>((Func<RoutePatternPart, string>) (p => p.DebuggerToString())));
  }

RoutePatternPart

  public abstract class RoutePatternPart
  {
    private protected RoutePatternPart(RoutePatternPartKind partKind) => this.PartKind = partKind;

    public RoutePatternPartKind PartKind { get; }

    public bool IsLiteral => this.PartKind == RoutePatternPartKind.Literal;

    public bool IsParameter => this.PartKind == RoutePatternPartKind.Parameter;

    public bool IsSeparator => this.PartKind == RoutePatternPartKind.Separator;

    internal abstract string DebuggerToString();
  }

RoutePatternFactory

非常重要的静态类,主要就是将字符串解析成RoutePattern,这边只展示最核心的一个方法PatternCore

image-20220407224747221

    private static RoutePattern PatternCore(
      string rawText,
      RouteValueDictionary defaults,
      RouteValueDictionary parameterPolicies,
      RouteValueDictionary requiredValues,
      IEnumerable<RoutePatternPathSegment> segments)
    {
      Dictionary<string, object> updatedDefaults = (Dictionary<string, object>) null;
      if (defaults != null && defaults.Count > 0)
      {
        updatedDefaults = new Dictionary<string, object>(defaults.Count, (IEqualityComparer<string>) StringComparer.OrdinalIgnoreCase);
        foreach (KeyValuePair<string, object> keyValuePair in defaults)
          updatedDefaults.Add(keyValuePair.Key, keyValuePair.Value);
      }
      Dictionary<string, List<RoutePatternParameterPolicyReference>> updatedParameterPolicies = (Dictionary<string, List<RoutePatternParameterPolicyReference>>) null;
      if (parameterPolicies != null && parameterPolicies.Count > 0)
      {
        updatedParameterPolicies = new Dictionary<string, List<RoutePatternParameterPolicyReference>>(parameterPolicies.Count, (IEqualityComparer<string>) StringComparer.OrdinalIgnoreCase);
        foreach (KeyValuePair<string, object> parameterPolicy1 in parameterPolicies)
        {
          List<RoutePatternParameterPolicyReference> parameterPolicyReferenceList = new List<RoutePatternParameterPolicyReference>();
          if (parameterPolicy1.Value is IParameterPolicy parameterPolicy3)
            parameterPolicyReferenceList.Add(RoutePatternFactory.ParameterPolicy(parameterPolicy3));
          else if (parameterPolicy1.Value is string)
          {
            parameterPolicyReferenceList.Add(RoutePatternFactory.Constraint(parameterPolicy1.Value));
          }
          else
          {
            if (!(parameterPolicy1.Value is IEnumerable enumerable))
              throw new InvalidOperationException(Resources.FormatRoutePattern_InvalidConstraintReference(parameterPolicy1.Value ?? (object) "null", (object) typeof (IRouteConstraint)));
            foreach (object constraint in enumerable)
              parameterPolicyReferenceList.Add(constraint is IParameterPolicy parameterPolicy2 ? RoutePatternFactory.ParameterPolicy(parameterPolicy2) : RoutePatternFactory.Constraint(constraint));
          }
          updatedParameterPolicies.Add(parameterPolicy1.Key, parameterPolicyReferenceList);
        }
      }
      List<RoutePatternParameterPart> patternParameterPartList = (List<RoutePatternParameterPart>) null;
      RoutePatternPathSegment[] array = segments.ToArray<RoutePatternPathSegment>();
      for (int index1 = 0; index1 < array.Length; ++index1)
      {
        RoutePatternPathSegment patternPathSegment = VisitSegment(array[index1]);
        array[index1] = patternPathSegment;
        for (int index2 = 0; index2 < patternPathSegment.Parts.Count; ++index2)
        {
          if (patternPathSegment.Parts[index2] is RoutePatternParameterPart part)
          {
            if (patternParameterPartList == null)
              patternParameterPartList = new List<RoutePatternParameterPart>();
            patternParameterPartList.Add(part);
          }
        }
      }
      if (requiredValues != null)
      {
        foreach (KeyValuePair<string, object> requiredValue in requiredValues)
        {
          bool flag = RouteValueEqualityComparer.Default.Equals((object) string.Empty, requiredValue.Value);
          if (!flag && patternParameterPartList != null)
          {
            for (int index = 0; index < patternParameterPartList.Count; ++index)
            {
              if (string.Equals(requiredValue.Key, patternParameterPartList[index].Name, StringComparison.OrdinalIgnoreCase))
              {
                flag = true;
                break;
              }
            }
          }
          object y;
          if (!flag && updatedDefaults != null && updatedDefaults.TryGetValue(requiredValue.Key, out y) && RouteValueEqualityComparer.Default.Equals(requiredValue.Value, y))
            flag = true;
          if (!flag)
            throw new InvalidOperationException("No corresponding parameter or default value could be found for the required value " + string.Format("'{0}={1}'. A non-null required value must correspond to a route parameter or the ", (object) requiredValue.Key, requiredValue.Value) + "route pattern must have a matching default value.");
        }
      }
      return new RoutePattern(rawText, (IReadOnlyDictionary<string, object>) updatedDefaults ?? RoutePatternFactory.EmptyDictionary, updatedParameterPolicies != null ? (IReadOnlyDictionary<string, IReadOnlyList<RoutePatternParameterPolicyReference>>) updatedParameterPolicies.ToDictionary<KeyValuePair<string, List<RoutePatternParameterPolicyReference>>, string, IReadOnlyList<RoutePatternParameterPolicyReference>>((Func<KeyValuePair<string, List<RoutePatternParameterPolicyReference>>, string>) (kvp => kvp.Key), (Func<KeyValuePair<string, List<RoutePatternParameterPolicyReference>>, IReadOnlyList<RoutePatternParameterPolicyReference>>) (kvp => (IReadOnlyList<RoutePatternParameterPolicyReference>) kvp.Value.ToArray())) : RoutePatternFactory.EmptyPoliciesDictionary, (IReadOnlyDictionary<string, object>) requiredValues ?? RoutePatternFactory.EmptyDictionary, (IReadOnlyList<RoutePatternParameterPart>) ((object) patternParameterPartList ?? (object) Array.Empty<RoutePatternParameterPart>()), (IReadOnlyList<RoutePatternPathSegment>) array);

EndpointRouteBuilderExtensions

该扩展方法展示如何封装将终结点封装,最主要的是最后的Map方法

  public static class EndpointRouteBuilderExtensions
  {
    private static readonly string[] GetVerb = new string[1]
    {
      "GET"
    };
    private static readonly string[] PostVerb = new string[1]
    {
      "POST"
    };
    private static readonly string[] PutVerb = new string[1]
    {
      "PUT"
    };
    private static readonly string[] DeleteVerb = new string[1]
    {
      "DELETE"
    };

    public static IEndpointConventionBuilder MapGet(
      this IEndpointRouteBuilder endpoints,
      string pattern,
      RequestDelegate requestDelegate)
    {
      return endpoints.MapMethods(pattern, (IEnumerable<string>) EndpointRouteBuilderExtensions.GetVerb, requestDelegate);
    }

    public static IEndpointConventionBuilder MapPost(
      this IEndpointRouteBuilder endpoints,
      string pattern,
      RequestDelegate requestDelegate)
    {
      return endpoints.MapMethods(pattern, (IEnumerable<string>) EndpointRouteBuilderExtensions.PostVerb, requestDelegate);
    }

    public static IEndpointConventionBuilder MapPut(
      this IEndpointRouteBuilder endpoints,
      string pattern,
      RequestDelegate requestDelegate)
    {
      return endpoints.MapMethods(pattern, (IEnumerable<string>) EndpointRouteBuilderExtensions.PutVerb, requestDelegate);
    }

    public static IEndpointConventionBuilder MapDelete(
      this IEndpointRouteBuilder endpoints,
      string pattern,
      RequestDelegate requestDelegate)
    {
      return endpoints.MapMethods(pattern, (IEnumerable<string>) EndpointRouteBuilderExtensions.DeleteVerb, requestDelegate);
    }

    public static IEndpointConventionBuilder MapMethods(
      this IEndpointRouteBuilder endpoints,
      string pattern,
      IEnumerable<string> httpMethods,
      RequestDelegate requestDelegate)
    {
      if (httpMethods == null)
        throw new ArgumentNullException(nameof (httpMethods));
      IEndpointConventionBuilder builder = endpoints.Map(RoutePatternFactory.Parse(pattern), requestDelegate);
      builder.WithDisplayName<IEndpointConventionBuilder>(pattern + " HTTP: " + string.Join(", ", httpMethods));
      builder.WithMetadata<IEndpointConventionBuilder>((object) new HttpMethodMetadata(httpMethods));
      return builder;
    }

    public static IEndpointConventionBuilder Map(
      this IEndpointRouteBuilder endpoints,
      string pattern,
      RequestDelegate requestDelegate)
    {
      return endpoints.Map(RoutePatternFactory.Parse(pattern), requestDelegate);
    }

    public static IEndpointConventionBuilder Map(
      this IEndpointRouteBuilder endpoints,
      RoutePattern pattern,
      RequestDelegate requestDelegate)
    {
      if (endpoints == null)
        throw new ArgumentNullException(nameof (endpoints));
      if (pattern == null)
        throw new ArgumentNullException(nameof (pattern));
      RouteEndpointBuilder routeEndpointBuilder1 = requestDelegate != null ? new RouteEndpointBuilder(requestDelegate, pattern, 0) : throw new ArgumentNullException(nameof (requestDelegate));
      routeEndpointBuilder1.DisplayName = pattern.RawText ?? pattern.DebuggerToString();
      RouteEndpointBuilder routeEndpointBuilder2 = routeEndpointBuilder1;
      IEnumerable<Attribute> customAttributes = requestDelegate.Method.GetCustomAttributes();
      if (customAttributes != null)
      {
        foreach (Attribute attribute in customAttributes)
          routeEndpointBuilder2.Metadata.Add((object) attribute);
      }
      ModelEndpointDataSource endpointDataSource = endpoints.DataSources.OfType<ModelEndpointDataSource>().FirstOrDefault<ModelEndpointDataSource>();
      if (endpointDataSource == null)
      {
        endpointDataSource = new ModelEndpointDataSource();
        endpoints.DataSources.Add((EndpointDataSource) endpointDataSource);
      }
      return endpointDataSource.AddEndpointBuilder((EndpointBuilder) routeEndpointBuilder2);
    }
  }

选择终结点中间件

概览

image-20220407225056097

选择的核心操作是由DfaMatcher完成的,它采用一种被称为确定有限状态自动机(Deterministic Finite Automaton,DFA)的形式从候选终结点中找到与当前匹配度最高的终结点,并将终结点添加到httpcontext中httpContext.SetEndpoint(local.Endpoint);

通过注入的MatcherFactory生成DataSourceDependentMatcher,然后利用其生成DfaMatcherBuilder,生成DfaMatcher,最后执行其MatchAsync

EndpointRoutingMiddleware

  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));
      this._matcherFactory = matcherFactory ?? throw new ArgumentNullException(nameof (matcherFactory));
      this._logger = (ILogger) (logger ?? throw new ArgumentNullException(nameof (logger)));
      this._diagnosticListener = diagnosticListener ?? throw new ArgumentNullException(nameof (diagnosticListener));
      this._next = next ?? throw new ArgumentNullException(nameof (next));
      this._endpointDataSource = (EndpointDataSource) new CompositeEndpointDataSource((IEnumerable<EndpointDataSource>) endpointRouteBuilder.DataSources);
    }

    public Task Invoke(HttpContext httpContext)
    {
      Endpoint endpoint = httpContext.GetEndpoint();
      if (endpoint != null)
      {
        EndpointRoutingMiddleware.Log.MatchSkipped(this._logger, endpoint);
        return this._next(httpContext);
      }
      Task<Matcher> matcherTask = this.InitializeAsync();
      if (!matcherTask.IsCompletedSuccessfully)
        return AwaitMatcher(this, httpContext, matcherTask);
      Task matchTask = matcherTask.Result.MatchAsync(httpContext);
      return !matchTask.IsCompletedSuccessfully ? AwaitMatch(this, httpContext, matchTask) : this.SetRoutingAndContinue(httpContext);

      static async Task AwaitMatcher(
        EndpointRoutingMiddleware middleware,
        HttpContext httpContext,
        Task<Matcher> matcherTask)
      {
        await (await matcherTask).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)
    {
      Endpoint endpoint = httpContext.GetEndpoint();
      if (endpoint == null)
      {
        EndpointRoutingMiddleware.Log.MatchFailure(this._logger);
      }
      else
      {
        if (this._diagnosticListener.IsEnabled() && this._diagnosticListener.IsEnabled("Microsoft.AspNetCore.Routing.EndpointMatched"))
          this._diagnosticListener.Write("Microsoft.AspNetCore.Routing.EndpointMatched", (object) httpContext);
        EndpointRoutingMiddleware.Log.MatchSuccess(this._logger, endpoint);
      }
      return this._next(httpContext);
    }

    private Task<Matcher> InitializeAsync() => this._initializationTask ?? this.InitializeCoreAsync();

    private Task<Matcher> InitializeCoreAsync()
    {
      TaskCompletionSource<Matcher> completionSource = new TaskCompletionSource<Matcher>(TaskCreationOptions.RunContinuationsAsynchronously);
      Task<Matcher> task = Interlocked.CompareExchange<Task<Matcher>>(ref this._initializationTask, completionSource.Task, (Task<Matcher>) null);
      if (task != null)
        return task;
      try
      {
        Matcher matcher = this._matcherFactory.CreateMatcher(this._endpointDataSource);
        using (ExecutionContext.SuppressFlow())
          this._initializationTask = Task.FromResult<Matcher>(matcher);
        completionSource.SetResult(matcher);
        return completionSource.Task;
      }
      catch (Exception ex)
      {
        this._initializationTask = (Task<Matcher>) null;
        completionSource.SetException(ex);
        return completionSource.Task;
      }
    }

    private static class Log
    {
      private static readonly 
      #nullable disable
      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) => EndpointRoutingMiddleware.Log._matchSuccess(logger, endpoint.DisplayName, (Exception) null);

      public static void MatchFailure(ILogger logger) => EndpointRoutingMiddleware.Log._matchFailure(logger, (Exception) null);

      public static void MatchSkipped(ILogger logger, Endpoint endpoint) => EndpointRoutingMiddleware.Log._matchingSkipped(logger, endpoint.DisplayName, (Exception) null);
    }
  }

DfaMatcherFactory

在CreateMatcher方法中直接new出DataSourceDependentMatcher,并传入了DfaMatcherBuilder

  internal class DfaMatcherFactory : MatcherFactory
  {
    private readonly IServiceProvider _services;

    public DfaMatcherFactory(IServiceProvider services) => this._services = services != null ? services : throw new ArgumentNullException(nameof (services));

    public override Matcher CreateMatcher(EndpointDataSource dataSource)
    {
      if (dataSource == null)
        throw new ArgumentNullException(nameof (dataSource));
      DataSourceDependentMatcher.Lifetime requiredService = this._services.GetRequiredService<DataSourceDependentMatcher.Lifetime>();
      return (Matcher) new DataSourceDependentMatcher(dataSource, requiredService, (Func<MatcherBuilder>) (() => (MatcherBuilder) this._services.GetRequiredService<DfaMatcherBuilder>()));
    }
  }

DataSourceDependentMatcher

在MatchAsync方法中调用matcherBuilder.Build();matcherBuilder其实是DfaMatcherBuilder,也就是这行代码创建了DfaMatcher

  internal sealed class DataSourceDependentMatcher : Matcher
  {
    private readonly Func<MatcherBuilder> _matcherBuilderFactory;
    private readonly DataSourceDependentCache<Matcher> _cache;

    public DataSourceDependentMatcher(
      EndpointDataSource dataSource,
      DataSourceDependentMatcher.Lifetime lifetime,
      Func<MatcherBuilder> matcherBuilderFactory)
    {
      this._matcherBuilderFactory = matcherBuilderFactory;
      this._cache = new DataSourceDependentCache<Matcher>(dataSource, new Func<IReadOnlyList<Endpoint>, Matcher>(this.CreateMatcher));
      this._cache.EnsureInitialized();
      lifetime.Cache = this._cache;
    }

    internal Matcher CurrentMatcher => this._cache.Value;

    public override Task MatchAsync(HttpContext httpContext) => this.CurrentMatcher.MatchAsync(httpContext);

    private Matcher CreateMatcher(IReadOnlyList<Endpoint> endpoints)
    {
      MatcherBuilder matcherBuilder = this._matcherBuilderFactory();
      for (int index = 0; index < endpoints.Count; ++index)
      {
        if (endpoints[index] is RouteEndpoint endpoint)
        {
          ISuppressMatchingMetadata metadata = endpoint.Metadata.GetMetadata<ISuppressMatchingMetadata>();
          if ((metadata != null ? (!metadata.SuppressMatching ? 1 : 0) : 1) != 0)
            matcherBuilder.AddEndpoint(endpoint);
        }
      }
      return matcherBuilder.Build();
    }

    public sealed class Lifetime : IDisposable
    {
      private readonly object _lock = new object();
      private DataSourceDependentCache<Matcher>? _cache;
      private bool _disposed;

      public DataSourceDependentCache<Matcher>? Cache
      {
        get => this._cache;
        set
        {
          object obj = this._lock;
          bool lockTaken = false;
          try
          {
            Monitor.Enter(obj, ref lockTaken);
            if (this._disposed && value != null)
              value.Dispose();
            this._cache = value;
          }
          finally
          {
            if (lockTaken)
              Monitor.Exit(obj);
          }
        }
      }

      public void Dispose()
      {
        object obj = this._lock;
        bool lockTaken = false;
        try
        {
          Monitor.Enter(obj, ref lockTaken);
          this._cache?.Dispose();
          this._cache = (DataSourceDependentCache<Matcher>) null;
          this._disposed = true;
        }
        finally
        {
          if (lockTaken)
            Monitor.Exit(obj);
        }
      }
    }
  }

DfaMatcher

核心逻辑,

    public override sealed unsafe Task MatchAsync(HttpContext httpContext)
    {
      if (httpContext == null)
        throw new ArgumentNullException(nameof (httpContext));
      bool flag1 = this._logger.IsEnabled(LogLevel.Debug);
      string path = httpContext.Request.Path.Value;
      int maxSegmentCount = this._maxSegmentCount;
      // ISSUE: untyped stack allocation
      Span<PathSegment> segments1 = new Span<PathSegment>((void*) __untypedstackalloc(checked (unchecked ((IntPtr) (uint) maxSegmentCount) * sizeof (PathSegment))), maxSegmentCount);
      int length1 = FastPathTokenizer.Tokenize(path, segments1);
      Span<PathSegment> segments2 = segments1.Slice(0, length1);
      (Candidate[] candidates, IEndpointSelectorPolicy[] policies) = this.FindCandidateSet(httpContext, path, (ReadOnlySpan<PathSegment>) segments2);
      int length2 = candidates.Length;
      if (length2 == 0)
      {
        if (flag1)
          DfaMatcher.Logger.CandidatesNotFound(this._logger, path);
        return Task.CompletedTask;
      }
      if (flag1)
        DfaMatcher.Logger.CandidatesFound(this._logger, path, candidates);
      int length3 = policies.Length;
      if (length2 == 1 && length3 == 0 && this._isDefaultEndpointSelector)
      {
        ref Candidate local = ref candidates[0];
        if (local.Flags == Candidate.CandidateFlags.None)
        {
          httpContext.SetEndpoint(local.Endpoint);
          return Task.CompletedTask;
        }
      }
      CandidateState[] candidateStateArray = new CandidateState[length2];
      for (int index = 0; index < length2; ++index)
      {
        ref Candidate local1 = ref candidates[index];
        ref CandidateState local2 = ref candidateStateArray[index];
        local2 = new CandidateState(local1.Endpoint, local1.Score);
        Candidate.CandidateFlags flags = local1.Flags;
        if ((flags & Candidate.CandidateFlags.HasSlots) != Candidate.CandidateFlags.None)
        {
          KeyValuePair<string, object>[] slots = local1.Slots;
          KeyValuePair<string, object>[] keyValuePairArray = new KeyValuePair<string, object>[slots.Length];
          if ((flags & Candidate.CandidateFlags.HasDefaults) != Candidate.CandidateFlags.None)
            Array.Copy((Array) slots, 0, (Array) keyValuePairArray, 0, slots.Length);
          if ((flags & Candidate.CandidateFlags.HasCaptures) != Candidate.CandidateFlags.None)
            this.ProcessCaptures(keyValuePairArray, local1.Captures, path, (ReadOnlySpan<PathSegment>) segments2);
          if ((flags & Candidate.CandidateFlags.HasCatchAll) != Candidate.CandidateFlags.None)
            this.ProcessCatchAll(keyValuePairArray, in local1.CatchAll, path, (ReadOnlySpan<PathSegment>) segments2);
          local2.Values = RouteValueDictionary.FromArray(keyValuePairArray);
        }
        bool flag2 = true;
        RouteValueDictionary routeValueDictionary;
        if ((flags & Candidate.CandidateFlags.HasComplexSegments) != Candidate.CandidateFlags.None)
        {
          ref CandidateState local3 = ref local2;
          if (local3.Values == null)
            local3.Values = routeValueDictionary = new RouteValueDictionary();
          if (!this.ProcessComplexSegments(local1.Endpoint, local1.ComplexSegments, path, (ReadOnlySpan<PathSegment>) segments2, local2.Values))
          {
            CandidateSet.SetValidity(ref local2, false);
            flag2 = false;
          }
        }
        if ((flags & Candidate.CandidateFlags.HasConstraints) != Candidate.CandidateFlags.None)
        {
          ref CandidateState local4 = ref local2;
          if (local4.Values == null)
            local4.Values = routeValueDictionary = new RouteValueDictionary();
          if (!this.ProcessConstraints(local1.Endpoint, local1.Constraints, httpContext, local2.Values))
          {
            CandidateSet.SetValidity(ref local2, false);
            flag2 = false;
          }
        }
        if (flag1)
        {
          if (flag2)
            DfaMatcher.Logger.CandidateValid(this._logger, path, local1.Endpoint);
          else
            DfaMatcher.Logger.CandidateNotValid(this._logger, path, local1.Endpoint);
        }
      }
      if (length3 == 0 && this._isDefaultEndpointSelector)
      {
        DefaultEndpointSelector.Select(httpContext, candidateStateArray);
        return Task.CompletedTask;
      }
      return length3 == 0 ? this._selector.SelectAsync(httpContext, new CandidateSet(candidateStateArray)) : this.SelectEndpointWithPoliciesAsync(httpContext, policies, new CandidateSet(candidateStateArray));
    }

执行终结点中间件

EndpointMiddleware

这个逻辑比较简单

  • 从HttpContext获取终结点
  • 执行
  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)
    {
      this._logger = (ILogger) (logger ?? throw new ArgumentNullException(nameof (logger)));
      this._next = next ?? throw new ArgumentNullException(nameof (next));
      this._routeOptions = routeOptions?.Value ?? throw new ArgumentNullException(nameof (routeOptions));
    }

    public Task Invoke(HttpContext httpContext)
    {
      Endpoint endpoint = httpContext.GetEndpoint();
      if (endpoint == null || endpoint.RequestDelegate == null)
        return this._next(httpContext);
      if (!this._routeOptions.SuppressCheckForUnhandledSecurityMetadata)
      {
        if (endpoint.Metadata.GetMetadata<IAuthorizeData>() != null && !httpContext.Items.ContainsKey((object) "__AuthorizationMiddlewareWithEndpointInvoked"))
          EndpointMiddleware.ThrowMissingAuthMiddlewareException(endpoint);
        if (endpoint.Metadata.GetMetadata<ICorsMetadata>() != null && !httpContext.Items.ContainsKey((object) "__CorsMiddlewareWithEndpointInvoked"))
          EndpointMiddleware.ThrowMissingCorsMiddlewareException(endpoint);
      }
      EndpointMiddleware.Log.ExecutingEndpoint(this._logger, endpoint);
      try
      {
        Task requestTask = endpoint.RequestDelegate(httpContext);
        if (!requestTask.IsCompletedSuccessfully)
          return AwaitRequestTask(endpoint, requestTask, this._logger);
      }
      catch (Exception ex)
      {
        EndpointMiddleware.Log.ExecutedEndpoint(this._logger, endpoint);
        return Task.FromException(ex);
      }
      EndpointMiddleware.Log.ExecutedEndpoint(this._logger, endpoint);
      return Task.CompletedTask;

      static async Task AwaitRequestTask(Endpoint endpoint, Task requestTask, ILogger logger)
      {
        try
        {
          await requestTask;
        }
        finally
        {
          EndpointMiddleware.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.UseCors() must appear between app.UseRouting() and app.UseEndpoints(...).");

    private static class Log
    {
      private static readonly 
      #nullable disable
      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) => EndpointMiddleware.Log._executingEndpoint(logger, endpoint.DisplayName, (Exception) null);

      public static void ExecutedEndpoint(ILogger logger, Endpoint endpoint) => EndpointMiddleware.Log._executedEndpoint(logger, endpoint.DisplayName, (Exception) null);
    }
  }
posted @ 2022-04-07 23:40  阿杜888  阅读(209)  评论(0编辑  收藏  举报