diff --git a/src/Microsoft.AspNetCore.Routing/DispatcherMiddleware.cs b/src/Microsoft.AspNetCore.Routing/DispatcherMiddleware.cs index 885d673028..68328c49ec 100644 --- a/src/Microsoft.AspNetCore.Routing/DispatcherMiddleware.cs +++ b/src/Microsoft.AspNetCore.Routing/DispatcherMiddleware.cs @@ -64,7 +64,11 @@ namespace Microsoft.AspNetCore.Routing await matcher.MatchAsync(httpContext, feature); if (feature.Endpoint != null) { - Log.EndpointMatched(_logger, feature); + Log.MatchSuccess(_logger, feature); + } + else + { + Log.MatchFailure(_logger); } await _next(httpContext); @@ -101,14 +105,24 @@ namespace Microsoft.AspNetCore.Routing private static class Log { private static readonly Action _matchSuccess = LoggerMessage.Define( - LogLevel.Information, - new EventId(0, "MatchSuccess"), + LogLevel.Debug, + new EventId(1, "MatchSuccess"), "Request matched endpoint '{EndpointName}'."); - public static void EndpointMatched(ILogger logger, EndpointFeature feature) + private static readonly Action _matchFailure = LoggerMessage.Define( + LogLevel.Debug, + new EventId(2, "MatchFailure"), + "Request did not match any endpoints."); + + public static void MatchSuccess(ILogger logger, EndpointFeature feature) { _matchSuccess(logger, feature.Endpoint.DisplayName, null); } + + public static void MatchFailure(ILogger logger) + { + _matchFailure(logger, null); + } } } } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/DispatcherMiddlewareTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/DispatcherMiddlewareTest.cs new file mode 100644 index 0000000000..7d93f42951 --- /dev/null +++ b/test/Microsoft.AspNetCore.Routing.Tests/DispatcherMiddlewareTest.cs @@ -0,0 +1,72 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing.Matchers; +using Microsoft.AspNetCore.Routing.TestObjects; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Logging.Testing; +using Microsoft.Extensions.Options; +using Xunit; + +namespace Microsoft.AspNetCore.Routing +{ + public class DispatcherMiddlewareTest + { + [Fact] + public async Task Invoke_OnCall_SetsEndpointFeature() + { + // Arrange + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = new TestServiceProvider(); + + RequestDelegate next = (c) => Task.FromResult(null); + + var logger = new Logger(NullLoggerFactory.Instance); + var options = Options.Create(new DispatcherOptions()); + var matcherFactory = new TestMatcherFactory(false); + var middleware = new DispatcherMiddleware(matcherFactory, options, logger, next); + + // Act + await middleware.Invoke(httpContext); + + // Assert + var endpointFeature = httpContext.Features.Get(); + Assert.NotNull(endpointFeature); + } + + [Fact] + public async Task Invoke_OnCall_WritesToConfiguredLogger() + { + // Arrange + var expectedMessage = "Request matched endpoint 'Test endpoint'."; + + var sink = new TestSink( + TestSink.EnableWithTypeName, + TestSink.EnableWithTypeName); + var loggerFactory = new TestLoggerFactory(sink, enabled: true); + + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = new TestServiceProvider(); + + RequestDelegate next = (c) => Task.FromResult(null); + + var logger = new Logger(loggerFactory); + var options = Options.Create(new DispatcherOptions()); + var matcherFactory = new TestMatcherFactory(true); + var middleware = new DispatcherMiddleware(matcherFactory, options, logger, next); + + // Act + await middleware.Invoke(httpContext); + + // Assert + Assert.Empty(sink.Scopes); + var write = Assert.Single(sink.Writes); + Assert.Equal(expectedMessage, write.State?.ToString()); + } + } +} diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Microsoft.AspNetCore.Routing.Tests.csproj b/test/Microsoft.AspNetCore.Routing.Tests/Microsoft.AspNetCore.Routing.Tests.csproj index ee389c233d..afc33172aa 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Microsoft.AspNetCore.Routing.Tests.csproj +++ b/test/Microsoft.AspNetCore.Routing.Tests/Microsoft.AspNetCore.Routing.Tests.csproj @@ -1,7 +1,8 @@ - + $(StandardTestTfms) + Microsoft.AspNetCore.Routing diff --git a/test/Microsoft.AspNetCore.Routing.Tests/RouterMiddlewareTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/RouterMiddlewareTest.cs index 464a389b44..8159c294e3 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/RouterMiddlewareTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/RouterMiddlewareTest.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Routing public class RouterMiddlewareTest { [Fact] - public async void Invoke_LogsCorrectValues_WhenNotHandled() + public async Task Invoke_LogsCorrectValues_WhenNotHandled() { // Arrange var expectedMessage = "Request did not match any routes."; @@ -45,7 +45,7 @@ namespace Microsoft.AspNetCore.Routing } [Fact] - public async void Invoke_DoesNotLog_WhenHandled() + public async Task Invoke_DoesNotLog_WhenHandled() { // Arrange var isHandled = true; diff --git a/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestEndpoint.cs b/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestEndpoint.cs new file mode 100644 index 0000000000..6cce52a5de --- /dev/null +++ b/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestEndpoint.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Routing.TestObjects +{ + internal class TestEndpoint : Endpoint + { + public TestEndpoint(EndpointMetadataCollection metadata, string displayName) : base(metadata, displayName) + { + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestMatcher.cs b/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestMatcher.cs new file mode 100644 index 0000000000..3522a5ea53 --- /dev/null +++ b/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestMatcher.cs @@ -0,0 +1,31 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing.Matchers; + +namespace Microsoft.AspNetCore.Routing.TestObjects +{ + internal class TestMatcher : Matcher + { + private readonly bool _isHandled; + + public TestMatcher(bool isHandled) + { + _isHandled = isHandled; + } + + public override Task MatchAsync(HttpContext httpContext, IEndpointFeature feature) + { + if (_isHandled) + { + feature.Endpoint = new TestEndpoint(EndpointMetadataCollection.Empty, "Test endpoint"); + } + + return Task.CompletedTask; + } + } +} diff --git a/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestMatcherFactory.cs b/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestMatcherFactory.cs new file mode 100644 index 0000000000..18d1f5d27a --- /dev/null +++ b/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestMatcherFactory.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Routing.Matchers; + +namespace Microsoft.AspNetCore.Routing.TestObjects +{ + internal class TestMatcherFactory : MatcherFactory + { + private readonly bool _isHandled; + + public TestMatcherFactory(bool isHandled) + { + _isHandled = isHandled; + } + + public override Matcher CreateMatcher(EndpointDataSource dataSource) + { + return new TestMatcher(_isHandled); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestServiceProvider.cs b/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestServiceProvider.cs new file mode 100644 index 0000000000..a9470edb24 --- /dev/null +++ b/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestServiceProvider.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNetCore.Routing.TestObjects +{ + internal class TestServiceProvider : IServiceProvider + { + public object GetService(Type serviceType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file