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,
|
||||
EventIds.TemplateFailedExpansion,
|
||||
"Failed to process the template {Template} for {Endpoint}. " +
|
||||
"The failure occured while expanding the template with values {Values}. " +
|
||||
"This is usually due to a missing or empty value in a complex segment.");
|
||||
"The failure occured while expanding the template with values {Values} " +
|
||||
"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>(
|
||||
LogLevel.Debug,
|
||||
|
|
|
|||
|
|
@ -57,12 +57,12 @@ namespace Microsoft.AspNetCore.Routing
|
|||
private static readonly Action<ILogger, string, Exception> _executingEndpoint = LoggerMessage.Define<string>(
|
||||
LogLevel.Information,
|
||||
new EventId(0, "ExecutingEndpoint"),
|
||||
"Executing endpoint '{EndpointName}'.");
|
||||
"Executing endpoint '{EndpointName}'");
|
||||
|
||||
private static readonly Action<ILogger, string, Exception> _executedEndpoint = LoggerMessage.Define<string>(
|
||||
LogLevel.Information,
|
||||
new EventId(1, "ExecutedEndpoint"),
|
||||
"Executed endpoint '{EndpointName}'.");
|
||||
"Executed endpoint '{EndpointName}'");
|
||||
|
||||
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>(
|
||||
LogLevel.Debug,
|
||||
new EventId(1, "MatchSuccess"),
|
||||
"Request matched endpoint '{EndpointName}'.");
|
||||
"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.");
|
||||
"Request did not match any endpoints");
|
||||
|
||||
public static void MatchSuccess(ILogger logger, EndpointSelectorContext context)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Routing.Logging
|
|||
LogLevel.Debug,
|
||||
1,
|
||||
"Route value '{RouteValue}' with key '{RouteKey}' did not match " +
|
||||
"the constraint '{RouteConstraint}'.");
|
||||
"the constraint '{RouteConstraint}'");
|
||||
}
|
||||
|
||||
public static void RouteValueDoesNotMatchConstraint(
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Routing.Logging
|
|||
_requestDidNotMatchRoutes = LoggerMessage.Define(
|
||||
LogLevel.Debug,
|
||||
1,
|
||||
"Request did not match any routes.");
|
||||
"Request did not match any routes");
|
||||
}
|
||||
|
||||
public static void RequestDidNotMatchRoutes(this ILogger logger)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Routing.Logging
|
|||
_matchedRoute = LoggerMessage.Define<string, string>(
|
||||
LogLevel.Debug,
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -6,17 +6,20 @@ using System.Collections.Generic;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing.Patterns;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing.Matching
|
||||
{
|
||||
internal sealed class DfaMatcher : Matcher
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly EndpointSelector _selector;
|
||||
private readonly DfaState[] _states;
|
||||
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;
|
||||
_states = states;
|
||||
_maxSegmentCount = maxSegmentCount;
|
||||
|
|
@ -34,6 +37,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
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
|
||||
// like creating substrings, creating route value dictionaries, and calling
|
||||
// into policies like versioning.
|
||||
|
|
@ -49,9 +55,19 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var candidates = FindCandidateSet(httpContext, path, segments);
|
||||
if (candidates.Length == 0)
|
||||
{
|
||||
if (log)
|
||||
{
|
||||
Logger.CandidatesNotFound(_logger, path);
|
||||
}
|
||||
|
||||
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
|
||||
// priority order.
|
||||
//
|
||||
|
|
@ -117,21 +133,34 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
// 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
|
||||
// route value dictionary.
|
||||
var isMatch = true;
|
||||
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);
|
||||
continue;
|
||||
isMatch = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & Candidate.CandidateFlags.HasConstraints) != 0)
|
||||
{
|
||||
if (!ProcessConstraints(candidate.Constraints, httpContext, values))
|
||||
if (!ProcessConstraints(candidate.Endpoint, candidate.Constraints, httpContext, values))
|
||||
{
|
||||
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(
|
||||
Endpoint endpoint,
|
||||
(RoutePatternPathSegment pathSegment, int segmentIndex)[] complexSegments,
|
||||
string path,
|
||||
ReadOnlySpan<PathSegment> segments,
|
||||
|
|
@ -209,10 +239,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
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);
|
||||
if (!RoutePatternMatcher.MatchComplexSegment(complexSegments[i].pathSegment, text, values))
|
||||
if (!RoutePatternMatcher.MatchComplexSegment(complexSegment, text, values))
|
||||
{
|
||||
Logger.CandidateRejectedByComplexSegment(_logger, path, endpoint, complexSegment);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -221,6 +253,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
}
|
||||
|
||||
private bool ProcessConstraints(
|
||||
Endpoint endpoint,
|
||||
KeyValuePair<string, IRouteConstraint>[] constraints,
|
||||
HttpContext httpContext,
|
||||
RouteValueDictionary values)
|
||||
|
|
@ -230,11 +263,112 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var constraint = constraints[i];
|
||||
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 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 Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing.Patterns;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing.Matching
|
||||
{
|
||||
|
|
@ -13,6 +14,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
private readonly List<RouteEndpoint> _endpoints = new List<RouteEndpoint>();
|
||||
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly ParameterPolicyFactory _parameterPolicyFactory;
|
||||
private readonly EndpointSelector _selector;
|
||||
private readonly MatcherPolicy[] _policies;
|
||||
|
|
@ -29,10 +31,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
private int _stateIndex;
|
||||
|
||||
public DfaMatcherBuilder(
|
||||
ILoggerFactory loggerFactory,
|
||||
ParameterPolicyFactory parameterPolicyFactory,
|
||||
EndpointSelector selector,
|
||||
IEnumerable<MatcherPolicy> policies)
|
||||
{
|
||||
_loggerFactory = loggerFactory;
|
||||
_parameterPolicyFactory = parameterPolicyFactory;
|
||||
_selector = selector;
|
||||
_policies = policies.OrderBy(p => p.Order).ToArray();
|
||||
|
|
@ -304,7 +308,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
JumpTableBuilder.Build(exitDestination, exitDestination, null),
|
||||
null);
|
||||
|
||||
return new DfaMatcher(_selector, states, maxSegmentCount);
|
||||
return new DfaMatcher(_loggerFactory.CreateLogger<DfaMatcher>(), _selector, states, maxSegmentCount);
|
||||
}
|
||||
|
||||
private int AddNode(
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
};
|
||||
var sink = SetUpMatch(constraints, loggerEnabled: true);
|
||||
var expectedMessage = "Route value 'value' with key 'b' did not match the constraint " +
|
||||
$"'{typeof(FailConstraint).FullName}'.";
|
||||
$"'{typeof(FailConstraint).FullName}'";
|
||||
|
||||
// Assert
|
||||
Assert.Empty(sink.Scopes);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
public async Task Invoke_OnCall_WritesToConfiguredLogger()
|
||||
{
|
||||
// Arrange
|
||||
var expectedMessage = "Request matched endpoint 'Test endpoint'.";
|
||||
var expectedMessage = "Request matched endpoint 'Test endpoint'";
|
||||
|
||||
var sink = new TestSink(
|
||||
TestSink.EnableWithTypeName<EndpointRoutingMiddleware>,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing.Patterns;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -117,6 +118,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
var dataSource = new CompositeEndpointDataSource(Array.Empty<EndpointDataSource>());
|
||||
return new DfaMatcherBuilder(
|
||||
NullLoggerFactory.Instance,
|
||||
Mock.Of<ParameterPolicyFactory>(),
|
||||
Mock.Of<EndpointSelector>(),
|
||||
policies);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing.Constraints;
|
||||
using Microsoft.AspNetCore.Routing.Patterns;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
|
@ -972,6 +973,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
{
|
||||
var dataSource = new CompositeEndpointDataSource(Array.Empty<EndpointDataSource>());
|
||||
return new DfaMatcherBuilder(
|
||||
NullLoggerFactory.Instance,
|
||||
new DefaultParameterPolicyFactory(Options.Create(new RouteOptions()), Mock.Of<IServiceProvider>()),
|
||||
Mock.Of<EndpointSelector>(),
|
||||
policies);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ using Microsoft.AspNetCore.Http;
|
|||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Routing.Patterns;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Testing;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -26,7 +28,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
template);
|
||||
}
|
||||
|
||||
private Matcher CreateDfaMatcher(EndpointDataSource dataSource, EndpointSelector endpointSelector = null)
|
||||
private Matcher CreateDfaMatcher(EndpointDataSource dataSource, EndpointSelector endpointSelector = null, ILoggerFactory loggerFactory = null)
|
||||
{
|
||||
var serviceCollection = new ServiceCollection()
|
||||
.AddLogging()
|
||||
|
|
@ -38,6 +40,11 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
serviceCollection.AddSingleton<EndpointSelector>(endpointSelector);
|
||||
}
|
||||
|
||||
if (loggerFactory != null)
|
||||
{
|
||||
serviceCollection.AddSingleton<ILoggerFactory>(loggerFactory);
|
||||
}
|
||||
|
||||
var services = serviceCollection.BuildServiceProvider();
|
||||
|
||||
var factory = services.GetRequiredService<MatcherFactory>();
|
||||
|
|
@ -76,14 +83,14 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
|
||||
var matcher = CreateDfaMatcher(endpointDataSource);
|
||||
|
||||
var (httpContext, endpointFeature) = CreateContext();
|
||||
var (httpContext, context) = CreateContext();
|
||||
httpContext.Request.Path = "/One";
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, endpointFeature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
Assert.Null(endpointFeature.Endpoint);
|
||||
Assert.Null(context.Endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -101,14 +108,14 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
|
||||
var matcher = CreateDfaMatcher(endpointDataSource);
|
||||
|
||||
var (httpContext, endpointFeature) = CreateContext();
|
||||
var (httpContext, context) = CreateContext();
|
||||
httpContext.Request.Path = "/Teams";
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, endpointFeature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(lowerOrderEndpoint, endpointFeature.Endpoint);
|
||||
Assert.Equal(lowerOrderEndpoint, context.Endpoint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -147,14 +154,181 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
|
||||
var matcher = CreateDfaMatcher(endpointDataSource, endpointSelector.Object);
|
||||
|
||||
var (httpContext, endpointFeature) = CreateContext();
|
||||
var (httpContext, context) = CreateContext();
|
||||
httpContext.Request.Path = "/Teams";
|
||||
|
||||
// Act
|
||||
await matcher.MatchAsync(httpContext, endpointFeature);
|
||||
await matcher.MatchAsync(httpContext, context);
|
||||
|
||||
// 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()
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
public async Task Invoke_LogsCorrectValues_WhenNotHandled()
|
||||
{
|
||||
// Arrange
|
||||
var expectedMessage = "Request did not match any routes.";
|
||||
var expectedMessage = "Request did not match any routes";
|
||||
var isHandled = false;
|
||||
|
||||
var sink = new TestSink(
|
||||
|
|
|
|||
Loading…
Reference in New Issue