Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/Http/Routing/src/EndpointRoutingMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -13,9 +14,12 @@ namespace Microsoft.AspNetCore.Routing
{
internal sealed class EndpointRoutingMiddleware
{
private const string DiagnosticsRouteMatchedKey = "Microsoft.AspNetCore.Routing.RouteMatched";

private readonly MatcherFactory _matcherFactory;
private readonly ILogger _logger;
private readonly EndpointDataSource _endpointDataSource;
private readonly DiagnosticListener _diagnosticListener;
private readonly RequestDelegate _next;

private Task<Matcher> _initializationTask;
Expand All @@ -24,6 +28,7 @@ public EndpointRoutingMiddleware(
MatcherFactory matcherFactory,
ILogger<EndpointRoutingMiddleware> logger,
IEndpointRouteBuilder endpointRouteBuilder,
DiagnosticListener diagnosticListener,
RequestDelegate next)
{
if (matcherFactory == null)
Expand All @@ -48,6 +53,7 @@ public EndpointRoutingMiddleware(

_matcherFactory = matcherFactory;
_logger = logger;
_diagnosticListener = diagnosticListener;
_next = next;

_endpointDataSource = new CompositeEndpointDataSource(endpointRouteBuilder.DataSources);
Expand Down Expand Up @@ -106,6 +112,13 @@ private Task SetRoutingAndContinue(HttpContext httpContext)
}
else
{
// Raise an event if the route matched
if (_diagnosticListener.IsEnabled() && _diagnosticListener.IsEnabled(DiagnosticsRouteMatchedKey))
{
// We're just going to send the HttpContext since it has all of the relevant information
_diagnosticListener.Write(DiagnosticsRouteMatchedKey, httpContext);
Copy link

@lmolkova lmolkova Jun 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Is it correct that this event will fire for both: WebApi and MVC?
  2. How do we get route from HttpContext? httpContext.GetEndpoint()?
  3. AppInsights uses https://github.com/aspnet/Mvc/blob/master/src/Microsoft.AspNetCore.Mvc.Core/MvcCoreDiagnosticSourceExtensions.cs events to know about routing today. Should we stop listening to those?

Copy link
Member

@JamesNK JamesNK Jun 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. It will fire for any framework that matches using endpoint routing. Today that means MVC, razor pages, gRPC, SignalR. Likely community frameworks will use it as well. On the endpoint there will be metadata that will let you figure out what the route is to. MVC for example will put the ActionDescriptor in the endpoint metadata. gRPC adds its own metadata, etc.
  2. Yes
  3. It should still be used for legacy apps. You should be able to listen for both side-by-side. I can't imagine why an ASP.NET Core app would use both old and new routing.

}

Log.MatchSuccess(_logger, endpoint);
}

Expand Down
42 changes: 41 additions & 1 deletion src/Http/Routing/test/UnitTests/EndpointRoutingMiddlewareTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
Expand Down Expand Up @@ -57,16 +59,26 @@ public async Task Invoke_OnCall_WritesToConfiguredLogger()
{
// Arrange
var expectedMessage = "Request matched endpoint 'Test endpoint'";
bool eventFired = true;

var sink = new TestSink(
TestSink.EnableWithTypeName<EndpointRoutingMiddleware>,
TestSink.EnableWithTypeName<EndpointRoutingMiddleware>);
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var listener = new DiagnosticListener("TestListener");

listener.Subscribe(new DelegateObserver(pair =>
{
eventFired = true;

Assert.Equal("Microsoft.AspNetCore.Routing.RouteMatched", pair.Key);
Assert.IsAssignableFrom<HttpContext>(pair.Value);
}));

var httpContext = CreateHttpContext();

var logger = new Logger<EndpointRoutingMiddleware>(loggerFactory);
var middleware = CreateMiddleware(logger);
var middleware = CreateMiddleware(logger, listener: listener);

// Act
await middleware.Invoke(httpContext);
Expand All @@ -75,6 +87,7 @@ public async Task Invoke_OnCall_WritesToConfiguredLogger()
Assert.Empty(sink.Scopes);
var write = Assert.Single(sink.Writes);
Assert.Equal(expectedMessage, write.State?.ToString());
Assert.True(eventFired);
}

[Fact]
Expand Down Expand Up @@ -159,19 +172,46 @@ private HttpContext CreateHttpContext()
private EndpointRoutingMiddleware CreateMiddleware(
Logger<EndpointRoutingMiddleware> logger = null,
MatcherFactory matcherFactory = null,
DiagnosticListener listener = null,
RequestDelegate next = null)
{
next ??= c => Task.CompletedTask;
logger ??= new Logger<EndpointRoutingMiddleware>(NullLoggerFactory.Instance);
matcherFactory ??= new TestMatcherFactory(true);
listener ??= new DiagnosticListener("Microsoft.AspNet");

var middleware = new EndpointRoutingMiddleware(
matcherFactory,
logger,
new DefaultEndpointRouteBuilder(Mock.Of<IApplicationBuilder>()),
listener,
next);

return middleware;
}

private class DelegateObserver : IObserver<KeyValuePair<string, object>>
{
private readonly Action<KeyValuePair<string, object>> _onNext;

public DelegateObserver(Action<KeyValuePair<string,object>> onNext)
{
_onNext = onNext;
}
public void OnCompleted()
{

}

public void OnError(Exception error)
{

}

public void OnNext(KeyValuePair<string, object> value)
{
_onNext(value);
}
}
}
}