React to routing changes (#8303)

This commit is contained in:
James Newton-King 2018-08-22 17:15:09 +12:00 committed by GitHub
parent 5bd818bd64
commit cbe1526763
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 79 additions and 74 deletions

View File

@ -48,8 +48,8 @@
<MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>2.2.0-preview1-34967</MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>
<MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>2.2.0-preview1-34967</MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>
<MicrosoftAspNetCoreResponseCachingPackageVersion>2.2.0-preview1-34967</MicrosoftAspNetCoreResponseCachingPackageVersion>
<MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>2.2.0-preview1-34967</MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>
<MicrosoftAspNetCoreRoutingPackageVersion>2.2.0-preview1-34967</MicrosoftAspNetCoreRoutingPackageVersion>
<MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>2.2.0-a-preview2-matcherendpoint-rename-16892</MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>
<MicrosoftAspNetCoreRoutingPackageVersion>2.2.0-a-preview2-matcherendpoint-rename-16892</MicrosoftAspNetCoreRoutingPackageVersion>
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>2.2.0-preview1-34967</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.2.0-preview1-34967</MicrosoftAspNetCoreServerKestrelPackageVersion>
<MicrosoftAspNetCoreSessionPackageVersion>2.2.0-preview1-34967</MicrosoftAspNetCoreSessionPackageVersion>

View File

@ -6,11 +6,11 @@ using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.AspNetCore.Routing.Template;
using Microsoft.Extensions.Primitives;
@ -262,7 +262,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
return false;
}
private MatcherEndpoint CreateEndpoint(
private RouteEndpoint CreateEndpoint(
ActionDescriptor action,
string routeName,
string template,
@ -271,9 +271,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
object source,
bool suppressLinkGeneration)
{
RequestDelegate invokerDelegate = (context) =>
RequestDelegate requestDelegate = (context) =>
{
var values = context.Features.Get<IEndpointFeature>().Values;
var values = context.Features.Get<IRouteValuesFeature>().RouteValues;
var routeData = new RouteData();
foreach (var kvp in values)
{
@ -299,9 +299,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
source,
suppressLinkGeneration);
var endpoint = new MatcherEndpoint(
next => invokerDelegate,
RoutePatternFactory.Parse(template, defaults, constraints: null),
var endpoint = new RouteEndpoint(
requestDelegate,
RoutePatternFactory.Parse(template, defaults, parameterPolicies: null),
order,
metadataCollection,
action.DisplayName);

View File

@ -135,8 +135,8 @@ namespace Microsoft.AspNetCore.Mvc.Routing
private Endpoint CreateRejectionEndpoint()
{
return new MatcherEndpoint(
(next) => (context) =>
return new RouteEndpoint(
(context) =>
{
context.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType;
return Task.CompletedTask;

View File

@ -3,6 +3,7 @@
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;

View File

@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Assert
var endpoint = Assert.Single(endpoints);
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
var matcherEndpoint = Assert.IsType<RouteEndpoint>(endpoint);
var routeValuesAddressMetadata = matcherEndpoint.Metadata.GetMetadata<RouteValuesAddressMetadata>();
Assert.NotNull(routeValuesAddressMetadata);
@ -77,14 +77,17 @@ namespace Microsoft.AspNetCore.Mvc.Internal
}
[Fact]
public void Endpoints_InvokeReturnedEndpoint_ActionInvokerProviderCalled()
public async Task Endpoints_InvokeReturnedEndpoint_ActionInvokerProviderCalled()
{
// Arrange
var featureCollection = new FeatureCollection();
featureCollection.Set<IEndpointFeature>(new EndpointFeature
var endpointFeature = new EndpointFeature
{
Values = new RouteValueDictionary()
});
RouteValues = new RouteValueDictionary()
};
var featureCollection = new FeatureCollection();
featureCollection.Set<IEndpointFeature>(endpointFeature);
featureCollection.Set<IRouteValuesFeature>(endpointFeature);
var httpContextMock = new Mock<HttpContext>();
httpContextMock.Setup(m => m.Features).Returns(featureCollection);
@ -122,11 +125,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Assert
var endpoint = Assert.Single(endpoints);
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
var matcherEndpoint = Assert.IsType<RouteEndpoint>(endpoint);
var invokerDelegate = matcherEndpoint.Invoker((next) => Task.CompletedTask);
invokerDelegate(httpContextMock.Object);
await matcherEndpoint.RequestDelegate(httpContextMock.Object);
Assert.True(actionInvokerCalled);
}
@ -138,7 +139,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var featureCollection = new FeatureCollection();
featureCollection.Set<IEndpointFeature>(new EndpointFeature
{
Values = new RouteValueDictionary()
RouteValues = new RouteValueDictionary()
});
var httpContextMock = new Mock<HttpContext>();
@ -199,7 +200,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Assert
var inspectors = finalEndpointTemplates
.Select(t => new Action<Endpoint>(e => Assert.Equal(t, Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText)))
.Select(t => new Action<Endpoint>(e => Assert.Equal(t, Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText)))
.ToArray();
// Assert
@ -226,7 +227,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Assert
var inspectors = finalEndpointTemplates
.Select(t => new Action<Endpoint>(e => Assert.Equal(t, Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText)))
.Select(t => new Action<Endpoint>(e => Assert.Equal(t, Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText)))
.ToArray();
// Assert
@ -250,8 +251,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Assert
Assert.Collection(endpoints,
(e) => Assert.Equal("TestController", Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText),
(e) => Assert.Equal("TestController/TestAction", Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText));
(e) => Assert.Equal("TestController", Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText),
(e) => Assert.Equal("TestController/TestAction", Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText));
}
[Fact]
@ -278,8 +279,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Assert
Assert.Collection(endpoints1,
(e) => Assert.Equal("TestController", Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText),
(e) => Assert.Equal("TestController/TestAction", Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText));
(e) => Assert.Equal("TestController", Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText),
(e) => Assert.Equal("TestController/TestAction", Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText));
Assert.Same(endpoints1, endpoints2);
actionDescriptorCollectionProviderMock.VerifyGet(m => m.ActionDescriptors, Times.Once);
@ -320,8 +321,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var endpoints = dataSource.Endpoints;
Assert.Collection(endpoints,
(e) => Assert.Equal("TestController", Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText),
(e) => Assert.Equal("TestController/TestAction", Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText));
(e) => Assert.Equal("TestController", Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText),
(e) => Assert.Equal("TestController/TestAction", Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText));
actionDescriptorCollectionProviderMock
.Setup(m => m.ActionDescriptors)
@ -337,7 +338,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
Assert.NotSame(endpoints, newEndpoints);
Assert.Collection(newEndpoints,
(e) => Assert.Equal("NewTestController/NewTestAction", Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText));
(e) => Assert.Equal("NewTestController/NewTestAction", Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText));
}
[Fact]
@ -359,8 +360,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Assert
Assert.Collection(endpoints,
(e) => Assert.Equal("TestController/TestAction1", Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText),
(e) => Assert.Equal("TestController/TestAction2", Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText));
(e) => Assert.Equal("TestController/TestAction1", Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText),
(e) => Assert.Equal("TestController/TestAction2", Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText));
}
[Theory]
@ -383,7 +384,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var endpoints = dataSource.Endpoints;
var inspectors = finalEndpointTemplates
.Select(t => new Action<Endpoint>(e => Assert.Equal(t, Assert.IsType<MatcherEndpoint>(e).RoutePattern.RawText)))
.Select(t => new Action<Endpoint>(e => Assert.Equal(t, Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText)))
.ToArray();
// Assert
@ -405,7 +406,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Assert
var endpoint = Assert.Single(endpoints);
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
var matcherEndpoint = Assert.IsType<RouteEndpoint>(endpoint);
var routeValuesAddressNameMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
Assert.NotNull(routeValuesAddressNameMetadata);
Assert.Equal(string.Empty, routeValuesAddressNameMetadata.Name);
@ -430,7 +431,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
endpoints,
(ep) =>
{
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(ep);
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
var routeValuesAddressMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
Assert.NotNull(routeValuesAddressMetadata);
Assert.Equal("namedRoute", routeValuesAddressMetadata.Name);
@ -438,7 +439,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(ep);
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
var routeValuesAddressMetadata = matcherEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
Assert.NotNull(routeValuesAddressMetadata);
Assert.Equal("namedRoute", routeValuesAddressMetadata.Name);
@ -469,25 +470,25 @@ namespace Microsoft.AspNetCore.Mvc.Internal
endpoints,
(ep) =>
{
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(ep);
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("Home/Index/{id?}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(1, matcherEndpoint.Order);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(ep);
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("named/Home/Index/{id?}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(2, matcherEndpoint.Order);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(ep);
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("Products/Details/{id?}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(1, matcherEndpoint.Order);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(ep);
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("named/Products/Details/{id?}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(2, matcherEndpoint.Order);
});
@ -589,7 +590,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Assert
var endpoint = Assert.Single(endpoints);
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
var matcherEndpoint = Assert.IsType<RouteEndpoint>(endpoint);
Assert.Equal("Foo/Bar", matcherEndpoint.RoutePattern.RawText);
AssertIsSubset(expectedDefaults, matcherEndpoint.RoutePattern.Defaults);
}
@ -611,7 +612,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Assert
var endpoint = Assert.Single(endpoints);
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
var matcherEndpoint = Assert.IsType<RouteEndpoint>(endpoint);
Assert.Equal("Foo/Bar", matcherEndpoint.RoutePattern.RawText);
AssertIsSubset(expectedDefaults, matcherEndpoint.RoutePattern.Defaults);
}
@ -634,7 +635,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Assert
var endpoint = Assert.Single(endpoints);
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
var matcherEndpoint = Assert.IsType<RouteEndpoint>(endpoint);
Assert.Equal("Foo/Bar/{subscription=general}", matcherEndpoint.RoutePattern.RawText);
AssertIsSubset(expectedDefaults, matcherEndpoint.RoutePattern.Defaults);
}
@ -656,7 +657,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Assert
var endpoint = Assert.Single(endpoints);
var matcherEndpoint = Assert.IsType<MatcherEndpoint>(endpoint);
var matcherEndpoint = Assert.IsType<RouteEndpoint>(endpoint);
Assert.Equal("Foo/Bar", matcherEndpoint.RoutePattern.RawText);
AssertIsSubset(expectedDefaults, matcherEndpoint.RoutePattern.Defaults);
}

View File

@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
@ -369,11 +370,11 @@ namespace Microsoft.AspNetCore.Mvc.Routing
return httpContext;
}
private static MatcherEndpoint CreateEndpoint(ActionDescriptor action)
private static RouteEndpoint CreateEndpoint(ActionDescriptor action)
{
var metadata = new List<object>() { action, };
return new MatcherEndpoint(
(r) => null,
return new RouteEndpoint(
(context) => Task.CompletedTask,
RoutePatternFactory.Parse("/"),
0,
new EndpointMetadataCollection(metadata),

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Matching;
@ -212,7 +213,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
Assert.Equal(expected, actual);
}
private static MatcherEndpoint CreateEndpoint(string template, ConsumesMetadata consumesMetadata)
private static RouteEndpoint CreateEndpoint(string template, ConsumesMetadata consumesMetadata)
{
var metadata = new List<object>();
if (consumesMetadata != null)
@ -220,8 +221,8 @@ namespace Microsoft.AspNetCore.Mvc.Routing
metadata.Add(consumesMetadata);
}
return new MatcherEndpoint(
(next) => null,
return new RouteEndpoint(
(context) => Task.CompletedTask,
RoutePatternFactory.Parse(template),
0,
new EndpointMetadataCollection(metadata),

View File

@ -5,6 +5,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.AspNetCore.Routing.Patterns;
@ -60,8 +62,9 @@ namespace Microsoft.AspNetCore.Mvc.Routing
// Set the endpoint feature and current context just as a normal request to MVC app would be
var endpointFeature = new EndpointFeature();
urlHelper.ActionContext.HttpContext.Features.Set<IEndpointFeature>(endpointFeature);
urlHelper.ActionContext.HttpContext.Features.Set<IRouteValuesFeature>(endpointFeature);
endpointFeature.Endpoint = endpoint1;
endpointFeature.Values = new RouteValueDictionary
endpointFeature.RouteValues = new RouteValueDictionary
{
["controller"] = "Orders",
["action"] = "GetById",
@ -123,7 +126,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
protected override IUrlHelper CreateUrlHelper(string appRoot, string host, string protocol)
{
return CreateUrlHelper(Enumerable.Empty<MatcherEndpoint>(), appRoot, host, protocol);
return CreateUrlHelper(Enumerable.Empty<RouteEndpoint>(), appRoot, host, protocol);
}
protected override IUrlHelper CreateUrlHelperWithDefaultRoutes(string appRoot, string host, string protocol)
@ -139,8 +142,8 @@ namespace Microsoft.AspNetCore.Mvc.Routing
string template)
{
var endpoints = GetDefaultEndpoints();
endpoints.Add(new MatcherEndpoint(
next => httpContext => Task.CompletedTask,
endpoints.Add(new RouteEndpoint(
httpContext => Task.CompletedTask,
RoutePatternFactory.Parse(template),
0,
EndpointMetadataCollection.Empty,
@ -153,10 +156,8 @@ namespace Microsoft.AspNetCore.Mvc.Routing
var httpContext = actionContext.HttpContext;
httpContext.Features.Set<IEndpointFeature>(new EndpointFeature()
{
Endpoint = new MatcherEndpoint(
next => cntxt => Task.CompletedTask,
RoutePatternFactory.Parse("/"),
0,
Endpoint = new Endpoint(
context => Task.CompletedTask,
EndpointMetadataCollection.Empty,
null)
});
@ -187,7 +188,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
return CreateUrlHelper(actionContext);
}
private IUrlHelper CreateUrlHelper(IEnumerable<MatcherEndpoint> endpoints, ActionContext actionContext = null)
private IUrlHelper CreateUrlHelper(IEnumerable<RouteEndpoint> endpoints, ActionContext actionContext = null)
{
var serviceProvider = CreateServices(endpoints);
var httpContext = CreateHttpContext(serviceProvider, null, null, "http");
@ -196,7 +197,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
}
private IUrlHelper CreateUrlHelper(
IEnumerable<MatcherEndpoint> endpoints,
IEnumerable<RouteEndpoint> endpoints,
string appRoot,
string host,
string protocol)
@ -207,9 +208,9 @@ namespace Microsoft.AspNetCore.Mvc.Routing
return CreateUrlHelper(actionContext);
}
private List<MatcherEndpoint> GetDefaultEndpoints()
private List<RouteEndpoint> GetDefaultEndpoints()
{
var endpoints = new List<MatcherEndpoint>();
var endpoints = new List<RouteEndpoint>();
endpoints.Add(
CreateEndpoint(
"home/newaction/{id}",
@ -278,7 +279,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
return endpoints;
}
private MatcherEndpoint CreateEndpoint(
private RouteEndpoint CreateEndpoint(
string template,
object defaults = null,
object requiredValues = null,
@ -292,9 +293,9 @@ namespace Microsoft.AspNetCore.Mvc.Routing
new RouteValuesAddressMetadata(routeName, new RouteValueDictionary(requiredValues)));
}
return new MatcherEndpoint(
next => (httpContext) => Task.CompletedTask,
RoutePatternFactory.Parse(template, defaults, constraints: null),
return new RouteEndpoint(
(httpContext) => Task.CompletedTask,
RoutePatternFactory.Parse(template, defaults, parameterPolicies: null),
order,
metadataCollection,
null);
@ -315,11 +316,11 @@ namespace Microsoft.AspNetCore.Mvc.Routing
return services.BuildServiceProvider();
}
private MatcherEndpoint GetEndpoint(string name, string template, RouteValueDictionary defaults)
private RouteEndpoint GetEndpoint(string name, string template, RouteValueDictionary defaults)
{
return new MatcherEndpoint(
next => c => Task.CompletedTask,
RoutePatternFactory.Parse(template, defaults, constraints: null),
return new RouteEndpoint(
c => Task.CompletedTask,
RoutePatternFactory.Parse(template, defaults, parameterPolicies: null),
0,
EndpointMetadataCollection.Empty,
null);

View File

@ -1,8 +1,8 @@
// 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.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
namespace BasicWebSite
{

View File

@ -1,8 +1,8 @@
// 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.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
namespace RoutingWebSite
{

View File

@ -1,8 +1,8 @@
// 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.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
namespace VersioningWebSite
{