AspNetCore 路由 <3.0>
概览
路由系统将URL和终结点Endpoint之间进行映射。首先在构建系统时,需要生成所有的映射;在请求来的时候,找到对应的终结点;然后执行对应的终结点,这样路由就由3部分构成:
-
终结点模型
-
选择终结点中间件
-
执行终结点中间件
为什么将选择和执行分为两个中间件,因为在选择好之后可以执行其他中间件,执行终结点中间件必须是最后一个,因为它不会继续调用其他中间件。
终结点模型
概览
-
通过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
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);
}
}
选择终结点中间件
概览
选择的核心操作是由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);
}
}
您的资助是我最大的动力!
金额随意,欢迎来赏!
出处:https://www.cnblogs.com/lovexinyi/
版权:本文版权归作者和博客园共有
转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文链接;否则必究法律责任