Merge pull request #832 from dotnet-maestro-bot/merge/release/2.2-to-master
[automated] Merge branch 'release/2.2' => 'master'
This commit is contained in:
commit
3863601f1d
|
|
@ -421,8 +421,8 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
EventIds.TemplateFailedExpansion,
|
EventIds.TemplateFailedExpansion,
|
||||||
"Failed to process the template {Template} for {Endpoint}. " +
|
"Failed to process the template {Template} for {Endpoint}. " +
|
||||||
"The failure occured while expanding the template with values {Values}. " +
|
"The failure occured while expanding the template with values {Values} " +
|
||||||
"This is usually due to a missing or empty value in a complex segment.");
|
"This is usually due to a missing or empty value in a complex segment");
|
||||||
|
|
||||||
private static readonly Action<ILogger, IEnumerable<string>, string, Exception> _linkGenerationSucceeded = LoggerMessage.Define<IEnumerable<string>, string>(
|
private static readonly Action<ILogger, IEnumerable<string>, string, Exception> _linkGenerationSucceeded = LoggerMessage.Define<IEnumerable<string>, string>(
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
|
|
|
||||||
|
|
@ -57,12 +57,12 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
private static readonly Action<ILogger, string, Exception> _executingEndpoint = LoggerMessage.Define<string>(
|
private static readonly Action<ILogger, string, Exception> _executingEndpoint = LoggerMessage.Define<string>(
|
||||||
LogLevel.Information,
|
LogLevel.Information,
|
||||||
new EventId(0, "ExecutingEndpoint"),
|
new EventId(0, "ExecutingEndpoint"),
|
||||||
"Executing endpoint '{EndpointName}'.");
|
"Executing endpoint '{EndpointName}'");
|
||||||
|
|
||||||
private static readonly Action<ILogger, string, Exception> _executedEndpoint = LoggerMessage.Define<string>(
|
private static readonly Action<ILogger, string, Exception> _executedEndpoint = LoggerMessage.Define<string>(
|
||||||
LogLevel.Information,
|
LogLevel.Information,
|
||||||
new EventId(1, "ExecutedEndpoint"),
|
new EventId(1, "ExecutedEndpoint"),
|
||||||
"Executed endpoint '{EndpointName}'.");
|
"Executed endpoint '{EndpointName}'");
|
||||||
|
|
||||||
public static void ExecutingEndpoint(ILogger logger, Endpoint endpoint)
|
public static void ExecutingEndpoint(ILogger logger, Endpoint endpoint)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -119,12 +119,12 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
private static readonly Action<ILogger, string, Exception> _matchSuccess = LoggerMessage.Define<string>(
|
private static readonly Action<ILogger, string, Exception> _matchSuccess = LoggerMessage.Define<string>(
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
new EventId(1, "MatchSuccess"),
|
new EventId(1, "MatchSuccess"),
|
||||||
"Request matched endpoint '{EndpointName}'.");
|
"Request matched endpoint '{EndpointName}'");
|
||||||
|
|
||||||
private static readonly Action<ILogger, Exception> _matchFailure = LoggerMessage.Define(
|
private static readonly Action<ILogger, Exception> _matchFailure = LoggerMessage.Define(
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
new EventId(2, "MatchFailure"),
|
new EventId(2, "MatchFailure"),
|
||||||
"Request did not match any endpoints.");
|
"Request did not match any endpoints");
|
||||||
|
|
||||||
public static void MatchSuccess(ILogger logger, EndpointSelectorContext context)
|
public static void MatchSuccess(ILogger logger, EndpointSelectorContext context)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Routing.Logging
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
1,
|
1,
|
||||||
"Route value '{RouteValue}' with key '{RouteKey}' did not match " +
|
"Route value '{RouteValue}' with key '{RouteKey}' did not match " +
|
||||||
"the constraint '{RouteConstraint}'.");
|
"the constraint '{RouteConstraint}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RouteValueDoesNotMatchConstraint(
|
public static void RouteValueDoesNotMatchConstraint(
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Routing.Logging
|
||||||
_requestDidNotMatchRoutes = LoggerMessage.Define(
|
_requestDidNotMatchRoutes = LoggerMessage.Define(
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
1,
|
1,
|
||||||
"Request did not match any routes.");
|
"Request did not match any routes");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RequestDidNotMatchRoutes(this ILogger logger)
|
public static void RequestDidNotMatchRoutes(this ILogger logger)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Routing.Logging
|
||||||
_matchedRoute = LoggerMessage.Define<string, string>(
|
_matchedRoute = LoggerMessage.Define<string, string>(
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
1,
|
1,
|
||||||
"Request successfully matched the route with name '{RouteName}' and template '{RouteTemplate}'.");
|
"Request successfully matched the route with name '{RouteName}' and template '{RouteTemplate}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void MatchedRoute(
|
public static void MatchedRoute(
|
||||||
|
|
|
||||||
|
|
@ -6,17 +6,20 @@ using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Routing.Patterns;
|
using Microsoft.AspNetCore.Routing.Patterns;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Routing.Matching
|
namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
{
|
{
|
||||||
internal sealed class DfaMatcher : Matcher
|
internal sealed class DfaMatcher : Matcher
|
||||||
{
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
private readonly EndpointSelector _selector;
|
private readonly EndpointSelector _selector;
|
||||||
private readonly DfaState[] _states;
|
private readonly DfaState[] _states;
|
||||||
private readonly int _maxSegmentCount;
|
private readonly int _maxSegmentCount;
|
||||||
|
|
||||||
public DfaMatcher(EndpointSelector selector, DfaState[] states, int maxSegmentCount)
|
public DfaMatcher(ILogger<DfaMatcher> logger, EndpointSelector selector, DfaState[] states, int maxSegmentCount)
|
||||||
{
|
{
|
||||||
|
_logger = logger;
|
||||||
_selector = selector;
|
_selector = selector;
|
||||||
_states = states;
|
_states = states;
|
||||||
_maxSegmentCount = maxSegmentCount;
|
_maxSegmentCount = maxSegmentCount;
|
||||||
|
|
@ -34,6 +37,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// All of the logging we do here is at level debug, so we can get away with doing a single check.
|
||||||
|
var log = _logger.IsEnabled(LogLevel.Debug);
|
||||||
|
|
||||||
// The sequence of actions we take is optimized to avoid doing expensive work
|
// The sequence of actions we take is optimized to avoid doing expensive work
|
||||||
// like creating substrings, creating route value dictionaries, and calling
|
// like creating substrings, creating route value dictionaries, and calling
|
||||||
// into policies like versioning.
|
// into policies like versioning.
|
||||||
|
|
@ -49,9 +55,19 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
var candidates = FindCandidateSet(httpContext, path, segments);
|
var candidates = FindCandidateSet(httpContext, path, segments);
|
||||||
if (candidates.Length == 0)
|
if (candidates.Length == 0)
|
||||||
{
|
{
|
||||||
|
if (log)
|
||||||
|
{
|
||||||
|
Logger.CandidatesNotFound(_logger, path);
|
||||||
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (log)
|
||||||
|
{
|
||||||
|
Logger.CandidatesFound(_logger, path, candidates);
|
||||||
|
}
|
||||||
|
|
||||||
// At this point we have a candidate set, defined as a list of endpoints in
|
// At this point we have a candidate set, defined as a list of endpoints in
|
||||||
// priority order.
|
// priority order.
|
||||||
//
|
//
|
||||||
|
|
@ -117,21 +133,34 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
// Now that we have the route values, we need to process complex segments.
|
// Now that we have the route values, we need to process complex segments.
|
||||||
// Complex segments go through an old API that requires a fully-materialized
|
// Complex segments go through an old API that requires a fully-materialized
|
||||||
// route value dictionary.
|
// route value dictionary.
|
||||||
|
var isMatch = true;
|
||||||
if ((flags & Candidate.CandidateFlags.HasComplexSegments) != 0)
|
if ((flags & Candidate.CandidateFlags.HasComplexSegments) != 0)
|
||||||
{
|
{
|
||||||
if (!ProcessComplexSegments(candidate.ComplexSegments, path, segments, values))
|
if (!ProcessComplexSegments(candidate.Endpoint, candidate.ComplexSegments, path, segments, values))
|
||||||
{
|
{
|
||||||
candidateSet.SetValidity(i, false);
|
candidateSet.SetValidity(i, false);
|
||||||
continue;
|
isMatch = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & Candidate.CandidateFlags.HasConstraints) != 0)
|
if ((flags & Candidate.CandidateFlags.HasConstraints) != 0)
|
||||||
{
|
{
|
||||||
if (!ProcessConstraints(candidate.Constraints, httpContext, values))
|
if (!ProcessConstraints(candidate.Endpoint, candidate.Constraints, httpContext, values))
|
||||||
{
|
{
|
||||||
candidateSet.SetValidity(i, false);
|
candidateSet.SetValidity(i, false);
|
||||||
continue;
|
isMatch = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log)
|
||||||
|
{
|
||||||
|
if (isMatch)
|
||||||
|
{
|
||||||
|
Logger.CandidateValid(_logger, path, candidate.Endpoint);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.CandidateNotValid(_logger, path, candidate.Endpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -202,6 +231,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ProcessComplexSegments(
|
private bool ProcessComplexSegments(
|
||||||
|
Endpoint endpoint,
|
||||||
(RoutePatternPathSegment pathSegment, int segmentIndex)[] complexSegments,
|
(RoutePatternPathSegment pathSegment, int segmentIndex)[] complexSegments,
|
||||||
string path,
|
string path,
|
||||||
ReadOnlySpan<PathSegment> segments,
|
ReadOnlySpan<PathSegment> segments,
|
||||||
|
|
@ -209,10 +239,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
{
|
{
|
||||||
for (var i = 0; i < complexSegments.Length; i++)
|
for (var i = 0; i < complexSegments.Length; i++)
|
||||||
{
|
{
|
||||||
var segment = segments[complexSegments[i].segmentIndex];
|
(var complexSegment, var segmentIndex) = complexSegments[i];
|
||||||
|
var segment = segments[segmentIndex];
|
||||||
var text = path.Substring(segment.Start, segment.Length);
|
var text = path.Substring(segment.Start, segment.Length);
|
||||||
if (!RoutePatternMatcher.MatchComplexSegment(complexSegments[i].pathSegment, text, values))
|
if (!RoutePatternMatcher.MatchComplexSegment(complexSegment, text, values))
|
||||||
{
|
{
|
||||||
|
Logger.CandidateRejectedByComplexSegment(_logger, path, endpoint, complexSegment);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -221,6 +253,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ProcessConstraints(
|
private bool ProcessConstraints(
|
||||||
|
Endpoint endpoint,
|
||||||
KeyValuePair<string, IRouteConstraint>[] constraints,
|
KeyValuePair<string, IRouteConstraint>[] constraints,
|
||||||
HttpContext httpContext,
|
HttpContext httpContext,
|
||||||
RouteValueDictionary values)
|
RouteValueDictionary values)
|
||||||
|
|
@ -230,11 +263,112 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
var constraint = constraints[i];
|
var constraint = constraints[i];
|
||||||
if (!constraint.Value.Match(httpContext, NullRouter.Instance, constraint.Key, values, RouteDirection.IncomingRequest))
|
if (!constraint.Value.Match(httpContext, NullRouter.Instance, constraint.Key, values, RouteDirection.IncomingRequest))
|
||||||
{
|
{
|
||||||
|
Logger.CandidateRejectedByConstraint(_logger, httpContext.Request.Path, endpoint, constraint.Key, constraint.Value, values[constraint.Key]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static class EventIds
|
||||||
|
{
|
||||||
|
public static readonly EventId CandidatesNotFound = new EventId(1000, "CandidatesNotFound");
|
||||||
|
public static readonly EventId CandidatesFound = new EventId(1001, "CandidatesFound");
|
||||||
|
|
||||||
|
public static readonly EventId CandidateRejectedByComplexSegment = new EventId(1002, "CandidateRejectedByComplexSegment");
|
||||||
|
public static readonly EventId CandidateRejectedByConstraint = new EventId(1003, "CandidateRejectedByConstraint");
|
||||||
|
|
||||||
|
public static readonly EventId CandidateNotValid = new EventId(1004, "CandiateNotValid");
|
||||||
|
public static readonly EventId CandidateValid = new EventId(1005, "CandiateValid");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Logger
|
||||||
|
{
|
||||||
|
private static readonly Action<ILogger, string, Exception> _candidatesNotFound = LoggerMessage.Define<string>(
|
||||||
|
LogLevel.Debug,
|
||||||
|
EventIds.CandidatesNotFound,
|
||||||
|
"No candidates found for the request path '{Path}'");
|
||||||
|
|
||||||
|
private static readonly Action<ILogger, int, string, Exception> _candidatesFound = LoggerMessage.Define<int, string>(
|
||||||
|
LogLevel.Debug,
|
||||||
|
EventIds.CandidatesFound,
|
||||||
|
"{CandidateCount} candidate(s) found for the request path '{Path}'");
|
||||||
|
|
||||||
|
private static readonly Action<ILogger, string, string, string, string, Exception> _candidateRejectedByComplexSegment = LoggerMessage.Define<string, string, string, string>(
|
||||||
|
LogLevel.Debug,
|
||||||
|
EventIds.CandidateRejectedByComplexSegment,
|
||||||
|
"Endpoint '{Endpoint}' with route pattern '{RoutePattern}' was rejected by complex segment '{Segment}' for the request path '{Path}'");
|
||||||
|
|
||||||
|
private static readonly Action<ILogger, string, string, string, string, object, string, Exception> _candidateRejectedByConstraint = LoggerMessage.Define<string, string, string, string, object, string>(
|
||||||
|
LogLevel.Debug,
|
||||||
|
EventIds.CandidateRejectedByConstraint,
|
||||||
|
"Endpoint '{Endpoint}' with route pattern '{RoutePattern}' was rejected by constraint '{ConstraintName}':'{Constraint}' with value '{RouteValue}' for the request path '{Path}'");
|
||||||
|
|
||||||
|
private static readonly Action<ILogger, string, string, string, Exception> _candidateNotValid = LoggerMessage.Define<string, string, string>(
|
||||||
|
LogLevel.Debug,
|
||||||
|
EventIds.CandidateNotValid,
|
||||||
|
"Endpoint '{Endpoint}' with route pattern '{RoutePattern}' is not valid for the request path '{Path}'");
|
||||||
|
|
||||||
|
private static readonly Action<ILogger, string, string, string, Exception> _candidateValid = LoggerMessage.Define<string, string, string>(
|
||||||
|
LogLevel.Debug,
|
||||||
|
EventIds.CandidateValid,
|
||||||
|
"Endpoint '{Endpoint}' with route pattern '{RoutePattern}' is valid for the request path '{Path}'");
|
||||||
|
|
||||||
|
public static void CandidatesNotFound(ILogger logger, string path)
|
||||||
|
{
|
||||||
|
_candidatesNotFound(logger, path, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CandidatesFound(ILogger logger, string path, Candidate[] candidates)
|
||||||
|
{
|
||||||
|
_candidatesFound(logger, candidates.Length, path, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CandidateRejectedByComplexSegment(ILogger logger, string path, Endpoint endpoint, RoutePatternPathSegment segment)
|
||||||
|
{
|
||||||
|
// This should return a real pattern since we're processing complex segments.... but just in case.
|
||||||
|
if (logger.IsEnabled(LogLevel.Debug))
|
||||||
|
{
|
||||||
|
var routePattern = GetRoutePattern(endpoint);
|
||||||
|
_candidateRejectedByComplexSegment(logger, endpoint.DisplayName, routePattern, segment.DebuggerToString(), path, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CandidateRejectedByConstraint(ILogger logger, string path, Endpoint endpoint, string constraintName, IRouteConstraint constraint, object value)
|
||||||
|
{
|
||||||
|
// This should return a real pattern since we're processing constraints.... but just in case.
|
||||||
|
if (logger.IsEnabled(LogLevel.Debug))
|
||||||
|
{
|
||||||
|
var routePattern = GetRoutePattern(endpoint);
|
||||||
|
_candidateRejectedByConstraint(logger, endpoint.DisplayName, routePattern, constraintName, constraint.ToString(), value, path, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CandidateNotValid(ILogger logger, string path, Endpoint endpoint)
|
||||||
|
{
|
||||||
|
// This can be the fallback value because it really might not be a route endpoint
|
||||||
|
if (logger.IsEnabled(LogLevel.Debug))
|
||||||
|
{
|
||||||
|
var routePattern = GetRoutePattern(endpoint);
|
||||||
|
_candidateNotValid(logger, endpoint.DisplayName, routePattern, path, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CandidateValid(ILogger logger, string path, Endpoint endpoint)
|
||||||
|
{
|
||||||
|
// This can be the fallback value because it really might not be a route endpoint
|
||||||
|
if (logger.IsEnabled(LogLevel.Debug))
|
||||||
|
{
|
||||||
|
var routePattern = GetRoutePattern(endpoint);
|
||||||
|
_candidateValid(logger, endpoint.DisplayName, routePattern, path, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetRoutePattern(Endpoint endpoint)
|
||||||
|
{
|
||||||
|
return (endpoint as RouteEndpoint)?.RoutePattern?.RawText ?? "(none)";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Routing.Patterns;
|
using Microsoft.AspNetCore.Routing.Patterns;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Routing.Matching
|
namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
{
|
{
|
||||||
|
|
@ -13,6 +14,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
{
|
{
|
||||||
private readonly List<RouteEndpoint> _endpoints = new List<RouteEndpoint>();
|
private readonly List<RouteEndpoint> _endpoints = new List<RouteEndpoint>();
|
||||||
|
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
private readonly ParameterPolicyFactory _parameterPolicyFactory;
|
private readonly ParameterPolicyFactory _parameterPolicyFactory;
|
||||||
private readonly EndpointSelector _selector;
|
private readonly EndpointSelector _selector;
|
||||||
private readonly MatcherPolicy[] _policies;
|
private readonly MatcherPolicy[] _policies;
|
||||||
|
|
@ -29,10 +31,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
private int _stateIndex;
|
private int _stateIndex;
|
||||||
|
|
||||||
public DfaMatcherBuilder(
|
public DfaMatcherBuilder(
|
||||||
|
ILoggerFactory loggerFactory,
|
||||||
ParameterPolicyFactory parameterPolicyFactory,
|
ParameterPolicyFactory parameterPolicyFactory,
|
||||||
EndpointSelector selector,
|
EndpointSelector selector,
|
||||||
IEnumerable<MatcherPolicy> policies)
|
IEnumerable<MatcherPolicy> policies)
|
||||||
{
|
{
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
_parameterPolicyFactory = parameterPolicyFactory;
|
_parameterPolicyFactory = parameterPolicyFactory;
|
||||||
_selector = selector;
|
_selector = selector;
|
||||||
_policies = policies.OrderBy(p => p.Order).ToArray();
|
_policies = policies.OrderBy(p => p.Order).ToArray();
|
||||||
|
|
@ -304,7 +308,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
JumpTableBuilder.Build(exitDestination, exitDestination, null),
|
JumpTableBuilder.Build(exitDestination, exitDestination, null),
|
||||||
null);
|
null);
|
||||||
|
|
||||||
return new DfaMatcher(_selector, states, maxSegmentCount);
|
return new DfaMatcher(_loggerFactory.CreateLogger<DfaMatcher>(), _selector, states, maxSegmentCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int AddNode(
|
private int AddNode(
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
};
|
};
|
||||||
var sink = SetUpMatch(constraints, loggerEnabled: true);
|
var sink = SetUpMatch(constraints, loggerEnabled: true);
|
||||||
var expectedMessage = "Route value 'value' with key 'b' did not match the constraint " +
|
var expectedMessage = "Route value 'value' with key 'b' did not match the constraint " +
|
||||||
$"'{typeof(FailConstraint).FullName}'.";
|
$"'{typeof(FailConstraint).FullName}'";
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Empty(sink.Scopes);
|
Assert.Empty(sink.Scopes);
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
public async Task Invoke_OnCall_WritesToConfiguredLogger()
|
public async Task Invoke_OnCall_WritesToConfiguredLogger()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expectedMessage = "Request matched endpoint 'Test endpoint'.";
|
var expectedMessage = "Request matched endpoint 'Test endpoint'";
|
||||||
|
|
||||||
var sink = new TestSink(
|
var sink = new TestSink(
|
||||||
TestSink.EnableWithTypeName<EndpointRoutingMiddleware>,
|
TestSink.EnableWithTypeName<EndpointRoutingMiddleware>,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Routing.Patterns;
|
using Microsoft.AspNetCore.Routing.Patterns;
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -117,6 +118,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
{
|
{
|
||||||
var dataSource = new CompositeEndpointDataSource(Array.Empty<EndpointDataSource>());
|
var dataSource = new CompositeEndpointDataSource(Array.Empty<EndpointDataSource>());
|
||||||
return new DfaMatcherBuilder(
|
return new DfaMatcherBuilder(
|
||||||
|
NullLoggerFactory.Instance,
|
||||||
Mock.Of<ParameterPolicyFactory>(),
|
Mock.Of<ParameterPolicyFactory>(),
|
||||||
Mock.Of<EndpointSelector>(),
|
Mock.Of<EndpointSelector>(),
|
||||||
policies);
|
policies);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.Linq;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Routing.Constraints;
|
using Microsoft.AspNetCore.Routing.Constraints;
|
||||||
using Microsoft.AspNetCore.Routing.Patterns;
|
using Microsoft.AspNetCore.Routing.Patterns;
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -972,6 +973,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
{
|
{
|
||||||
var dataSource = new CompositeEndpointDataSource(Array.Empty<EndpointDataSource>());
|
var dataSource = new CompositeEndpointDataSource(Array.Empty<EndpointDataSource>());
|
||||||
return new DfaMatcherBuilder(
|
return new DfaMatcherBuilder(
|
||||||
|
NullLoggerFactory.Instance,
|
||||||
new DefaultParameterPolicyFactory(Options.Create(new RouteOptions()), Mock.Of<IServiceProvider>()),
|
new DefaultParameterPolicyFactory(Options.Create(new RouteOptions()), Mock.Of<IServiceProvider>()),
|
||||||
Mock.Of<EndpointSelector>(),
|
Mock.Of<EndpointSelector>(),
|
||||||
policies);
|
policies);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.Routing.Patterns;
|
using Microsoft.AspNetCore.Routing.Patterns;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Logging.Testing;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -26,7 +28,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
template);
|
template);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Matcher CreateDfaMatcher(EndpointDataSource dataSource, EndpointSelector endpointSelector = null)
|
private Matcher CreateDfaMatcher(EndpointDataSource dataSource, EndpointSelector endpointSelector = null, ILoggerFactory loggerFactory = null)
|
||||||
{
|
{
|
||||||
var serviceCollection = new ServiceCollection()
|
var serviceCollection = new ServiceCollection()
|
||||||
.AddLogging()
|
.AddLogging()
|
||||||
|
|
@ -38,6 +40,11 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
serviceCollection.AddSingleton<EndpointSelector>(endpointSelector);
|
serviceCollection.AddSingleton<EndpointSelector>(endpointSelector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (loggerFactory != null)
|
||||||
|
{
|
||||||
|
serviceCollection.AddSingleton<ILoggerFactory>(loggerFactory);
|
||||||
|
}
|
||||||
|
|
||||||
var services = serviceCollection.BuildServiceProvider();
|
var services = serviceCollection.BuildServiceProvider();
|
||||||
|
|
||||||
var factory = services.GetRequiredService<MatcherFactory>();
|
var factory = services.GetRequiredService<MatcherFactory>();
|
||||||
|
|
@ -76,14 +83,14 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
|
|
||||||
var matcher = CreateDfaMatcher(endpointDataSource);
|
var matcher = CreateDfaMatcher(endpointDataSource);
|
||||||
|
|
||||||
var (httpContext, endpointFeature) = CreateContext();
|
var (httpContext, context) = CreateContext();
|
||||||
httpContext.Request.Path = "/One";
|
httpContext.Request.Path = "/One";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await matcher.MatchAsync(httpContext, endpointFeature);
|
await matcher.MatchAsync(httpContext, context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Null(endpointFeature.Endpoint);
|
Assert.Null(context.Endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -101,14 +108,14 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
|
|
||||||
var matcher = CreateDfaMatcher(endpointDataSource);
|
var matcher = CreateDfaMatcher(endpointDataSource);
|
||||||
|
|
||||||
var (httpContext, endpointFeature) = CreateContext();
|
var (httpContext, context) = CreateContext();
|
||||||
httpContext.Request.Path = "/Teams";
|
httpContext.Request.Path = "/Teams";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await matcher.MatchAsync(httpContext, endpointFeature);
|
await matcher.MatchAsync(httpContext, context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(lowerOrderEndpoint, endpointFeature.Endpoint);
|
Assert.Equal(lowerOrderEndpoint, context.Endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -147,14 +154,181 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
|
|
||||||
var matcher = CreateDfaMatcher(endpointDataSource, endpointSelector.Object);
|
var matcher = CreateDfaMatcher(endpointDataSource, endpointSelector.Object);
|
||||||
|
|
||||||
var (httpContext, endpointFeature) = CreateContext();
|
var (httpContext, context) = CreateContext();
|
||||||
httpContext.Request.Path = "/Teams";
|
httpContext.Request.Path = "/Teams";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await matcher.MatchAsync(httpContext, endpointFeature);
|
await matcher.MatchAsync(httpContext, context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(endpoint2, endpointFeature.Endpoint);
|
Assert.Equal(endpoint2, context.Endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MatchAsync_NoCandidates_Logging()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var endpointDataSource = new DefaultEndpointDataSource(new List<Endpoint>
|
||||||
|
{
|
||||||
|
CreateEndpoint("/{p:int}", 0)
|
||||||
|
});
|
||||||
|
|
||||||
|
var sink = new TestSink();
|
||||||
|
var matcher = CreateDfaMatcher(endpointDataSource, loggerFactory: new TestLoggerFactory(sink, enabled: true));
|
||||||
|
|
||||||
|
var (httpContext, context) = CreateContext();
|
||||||
|
httpContext.Request.Path = "/";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await matcher.MatchAsync(httpContext, context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(context.Endpoint);
|
||||||
|
|
||||||
|
Assert.Collection(
|
||||||
|
sink.Writes,
|
||||||
|
(log) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(DfaMatcher.EventIds.CandidatesNotFound, log.EventId);
|
||||||
|
Assert.Equal("No candidates found for the request path '/'", log.Message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MatchAsync_ConstraintRejectsEndpoint_Logging()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var endpointDataSource = new DefaultEndpointDataSource(new List<Endpoint>
|
||||||
|
{
|
||||||
|
CreateEndpoint("/{p:int}", 0)
|
||||||
|
});
|
||||||
|
|
||||||
|
var sink = new TestSink();
|
||||||
|
var matcher = CreateDfaMatcher(endpointDataSource, loggerFactory: new TestLoggerFactory(sink, enabled: true));
|
||||||
|
|
||||||
|
var (httpContext, context) = CreateContext();
|
||||||
|
httpContext.Request.Path = "/One";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await matcher.MatchAsync(httpContext, context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(context.Endpoint);
|
||||||
|
|
||||||
|
Assert.Collection(
|
||||||
|
sink.Writes,
|
||||||
|
(log) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(DfaMatcher.EventIds.CandidatesFound, log.EventId);
|
||||||
|
Assert.Equal("1 candidate(s) found for the request path '/One'", log.Message);
|
||||||
|
},
|
||||||
|
(log) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(DfaMatcher.EventIds.CandidateRejectedByConstraint, log.EventId);
|
||||||
|
Assert.Equal("Endpoint '/{p:int}' with route pattern '/{p:int}' was rejected by constraint 'p':'Microsoft.AspNetCore.Routing.Constraints.IntRouteConstraint' with value 'One' for the request path '/One'", log.Message);
|
||||||
|
},
|
||||||
|
(log) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(DfaMatcher.EventIds.CandidateNotValid, log.EventId);
|
||||||
|
Assert.Equal("Endpoint '/{p:int}' with route pattern '/{p:int}' is not valid for the request path '/One'", log.Message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MatchAsync_ComplexSegmentRejectsEndpoint_Logging()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var endpointDataSource = new DefaultEndpointDataSource(new List<Endpoint>
|
||||||
|
{
|
||||||
|
CreateEndpoint("/x-{id}-y", 0)
|
||||||
|
});
|
||||||
|
|
||||||
|
var sink = new TestSink();
|
||||||
|
var matcher = CreateDfaMatcher(endpointDataSource, loggerFactory: new TestLoggerFactory(sink, enabled: true));
|
||||||
|
|
||||||
|
var (httpContext, context) = CreateContext();
|
||||||
|
httpContext.Request.Path = "/One";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await matcher.MatchAsync(httpContext, context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(context.Endpoint);
|
||||||
|
|
||||||
|
Assert.Collection(
|
||||||
|
sink.Writes,
|
||||||
|
(log) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(DfaMatcher.EventIds.CandidatesFound, log.EventId);
|
||||||
|
Assert.Equal("1 candidate(s) found for the request path '/One'", log.Message);
|
||||||
|
},
|
||||||
|
(log) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(DfaMatcher.EventIds.CandidateRejectedByComplexSegment, log.EventId);
|
||||||
|
Assert.Equal("Endpoint '/x-{id}-y' with route pattern '/x-{id}-y' was rejected by complex segment 'x-{id}-y' for the request path '/One'", log.Message);
|
||||||
|
},
|
||||||
|
(log) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(DfaMatcher.EventIds.CandidateNotValid, log.EventId);
|
||||||
|
Assert.Equal("Endpoint '/x-{id}-y' with route pattern '/x-{id}-y' is not valid for the request path '/One'", log.Message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MatchAsync_MultipleCandidates_Logging()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var endpointDataSource = new DefaultEndpointDataSource(new List<Endpoint>
|
||||||
|
{
|
||||||
|
CreateEndpoint("/One", 0),
|
||||||
|
CreateEndpoint("/{p:int}", 0),
|
||||||
|
CreateEndpoint("/x-{id}-y", 0),
|
||||||
|
});
|
||||||
|
|
||||||
|
var sink = new TestSink();
|
||||||
|
var matcher = CreateDfaMatcher(endpointDataSource, loggerFactory: new TestLoggerFactory(sink, enabled: true));
|
||||||
|
|
||||||
|
var (httpContext, context) = CreateContext();
|
||||||
|
httpContext.Request.Path = "/One";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await matcher.MatchAsync(httpContext, context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Same(endpointDataSource.Endpoints[0], context.Endpoint);
|
||||||
|
|
||||||
|
Assert.Collection(
|
||||||
|
sink.Writes,
|
||||||
|
(log) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(DfaMatcher.EventIds.CandidatesFound, log.EventId);
|
||||||
|
Assert.Equal("3 candidate(s) found for the request path '/One'", log.Message);
|
||||||
|
},
|
||||||
|
(log) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(DfaMatcher.EventIds.CandidateValid, log.EventId);
|
||||||
|
Assert.Equal("Endpoint '/One' with route pattern '/One' is valid for the request path '/One'", log.Message);
|
||||||
|
},
|
||||||
|
(log) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(DfaMatcher.EventIds.CandidateRejectedByConstraint, log.EventId);
|
||||||
|
Assert.Equal("Endpoint '/{p:int}' with route pattern '/{p:int}' was rejected by constraint 'p':'Microsoft.AspNetCore.Routing.Constraints.IntRouteConstraint' with value 'One' for the request path '/One'", log.Message);
|
||||||
|
},
|
||||||
|
(log) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(DfaMatcher.EventIds.CandidateNotValid, log.EventId);
|
||||||
|
Assert.Equal("Endpoint '/{p:int}' with route pattern '/{p:int}' is not valid for the request path '/One'", log.Message);
|
||||||
|
},
|
||||||
|
(log) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(DfaMatcher.EventIds.CandidateRejectedByComplexSegment, log.EventId);
|
||||||
|
Assert.Equal("Endpoint '/x-{id}-y' with route pattern '/x-{id}-y' was rejected by complex segment 'x-{id}-y' for the request path '/One'", log.Message);
|
||||||
|
},
|
||||||
|
(log) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(DfaMatcher.EventIds.CandidateNotValid, log.EventId);
|
||||||
|
Assert.Equal("Endpoint '/x-{id}-y' with route pattern '/x-{id}-y' is not valid for the request path '/One'", log.Message);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private (HttpContext httpContext, EndpointSelectorContext context) CreateContext()
|
private (HttpContext httpContext, EndpointSelectorContext context) CreateContext()
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
public async Task Invoke_LogsCorrectValues_WhenNotHandled()
|
public async Task Invoke_LogsCorrectValues_WhenNotHandled()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expectedMessage = "Request did not match any routes.";
|
var expectedMessage = "Request did not match any routes";
|
||||||
var isHandled = false;
|
var isHandled = false;
|
||||||
|
|
||||||
var sink = new TestSink(
|
var sink = new TestSink(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue