diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 64ff041d5c..eac4268e4c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,4 +1,4 @@
Contributing
======
-Information on contributing to this repo is in the [Contributing Guide](https://github.com/aspnet/Home/blob/dev/CONTRIBUTING.md) in the Home repo.
+Information on contributing to this repo is in the [Contributing Guide](https://github.com/aspnet/Home/blob/master/CONTRIBUTING.md) in the Home repo.
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcApplicationBuilderExtensions.cs b/src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcApplicationBuilderExtensions.cs
index 74e5f8e5ec..0b190b0231 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcApplicationBuilderExtensions.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcApplicationBuilderExtensions.cs
@@ -4,10 +4,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Builder
{
@@ -16,6 +18,9 @@ namespace Microsoft.AspNetCore.Builder
///
public static class MvcApplicationBuilderExtensions
{
+ // Property key set in routing package by UseGlobalRouting to indicate middleware is registered
+ private const string GlobalRoutingRegisteredKey = "__GlobalRoutingMiddlewareRegistered";
+
///
/// Adds MVC to the request execution pipeline.
///
@@ -79,16 +84,91 @@ namespace Microsoft.AspNetCore.Builder
VerifyMvcIsRegistered(app);
- var routes = new RouteBuilder(app)
+ var options = app.ApplicationServices.GetRequiredService>();
+
+ if (options.Value.EnableGlobalRouting)
{
- DefaultHandler = app.ApplicationServices.GetRequiredService(),
- };
+ var mvcEndpointDataSource = app.ApplicationServices
+ .GetRequiredService>()
+ .OfType()
+ .First();
+ var constraintResolver = app.ApplicationServices
+ .GetRequiredService();
- configureRoutes(routes);
+ var endpointRouteBuilder = new EndpointRouteBuilder(app);
- routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(app.ApplicationServices));
+ configureRoutes(endpointRouteBuilder);
- return app.UseRouter(routes.Build());
+ foreach (var router in endpointRouteBuilder.Routes)
+ {
+ // Only accept Microsoft.AspNetCore.Routing.Route when converting to endpoint
+ // Sub-types could have additional customization that we can't knowingly convert
+ if (router is Route route && router.GetType() == typeof(Route))
+ {
+ var endpointInfo = new MvcEndpointInfo(
+ route.Name,
+ route.RouteTemplate,
+ route.Defaults,
+ route.Constraints.ToDictionary(kvp => kvp.Key, kvp => (object)kvp.Value),
+ route.DataTokens,
+ constraintResolver);
+
+ mvcEndpointDataSource.ConventionalEndpointInfos.Add(endpointInfo);
+ }
+ else
+ {
+ throw new InvalidOperationException($"Cannot use '{router.GetType().FullName}' with Global Routing.");
+ }
+ }
+
+ if (!app.Properties.TryGetValue(GlobalRoutingRegisteredKey, out _))
+ {
+ // Matching middleware has not been registered yet
+ // For back-compat register middleware so an endpoint is matched and then immediately used
+ app.UseGlobalRouting();
+ }
+
+ return app.UseEndpoint();
+ }
+ else
+ {
+ var routes = new RouteBuilder(app)
+ {
+ DefaultHandler = app.ApplicationServices.GetRequiredService(),
+ };
+
+ configureRoutes(routes);
+
+ routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(app.ApplicationServices));
+
+ return app.UseRouter(routes.Build());
+ }
+ }
+
+ private class EndpointRouteBuilder : IRouteBuilder
+ {
+ public EndpointRouteBuilder(IApplicationBuilder applicationBuilder)
+ {
+ ApplicationBuilder = applicationBuilder;
+ Routes = new List();
+ DefaultHandler = NullRouter.Instance;
+ }
+
+ public IApplicationBuilder ApplicationBuilder { get; }
+
+ public IRouter DefaultHandler { get; set; }
+
+ public IServiceProvider ServiceProvider
+ {
+ get { return ApplicationBuilder.ApplicationServices; }
+ }
+
+ public IList Routes { get; }
+
+ public IRouter Build()
+ {
+ throw new NotSupportedException();
+ }
}
public static IApplicationBuilder UseMvcWithEndpoint(
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcEndpointInfo.cs b/src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcEndpointInfo.cs
index bb6a9df95f..ba59c34b44 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcEndpointInfo.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcEndpointInfo.cs
@@ -94,10 +94,13 @@ namespace Microsoft.AspNetCore.Builder
{
if (parameter.DefaultValue != null)
{
- if (result.ContainsKey(parameter.Name))
+ if (result.TryGetValue(parameter.Name, out var value))
{
- throw new InvalidOperationException(
- string.Format(CultureInfo.CurrentCulture, "The route parameter '{0}' has both an inline default value and an explicit default value specified. A route parameter cannot contain an inline default value when a default value is specified explicitly. Consider removing one of them.", parameter.Name));
+ if (!object.Equals(value, parameter.DefaultValue))
+ {
+ throw new InvalidOperationException(
+ string.Format(CultureInfo.CurrentCulture, "The route parameter '{0}' has both an inline default value and an explicit default value specified. A route parameter cannot contain an inline default value when a default value is specified explicitly. Consider removing one of them.", parameter.Name));
+ }
}
else
{
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/MvcOptionsConfigureCompatibilityOptions.cs b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/MvcOptionsConfigureCompatibilityOptions.cs
index 71957b40cf..28fd0605e7 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/MvcOptionsConfigureCompatibilityOptions.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/MvcOptionsConfigureCompatibilityOptions.cs
@@ -32,6 +32,11 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
values[nameof(MvcOptions.SuppressBindingUndefinedValueToEnumType)] = true;
}
+ if (Version >= CompatibilityVersion.Version_2_2)
+ {
+ values[nameof(MvcOptions.EnableGlobalRouting)] = true;
+ }
+
return values;
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcEndpointDataSource.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcEndpointDataSource.cs
index f725da108b..d6bc8c8f68 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcEndpointDataSource.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcEndpointDataSource.cs
@@ -247,7 +247,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
//
// REVIEW: This is really ugly
if (endpointInfo.Constraints.TryGetValue(routeKey, out var constraint)
- && !constraint.Match(new DefaultHttpContext() { RequestServices = _serviceProvider }, new DummyRouter(), routeKey, new RouteValueDictionary(action.RouteValues), RouteDirection.IncomingRequest))
+ && !constraint.Match(new DefaultHttpContext() { RequestServices = _serviceProvider }, NullRouter.Instance, routeKey, new RouteValueDictionary(action.RouteValues), RouteDirection.IncomingRequest))
{
// Did not match constraint
return false;
@@ -260,19 +260,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
return false;
}
- private class DummyRouter : IRouter
- {
- public VirtualPathData GetVirtualPath(VirtualPathContext context)
- {
- return null;
- }
-
- public Task RouteAsync(RouteContext context)
- {
- return Task.CompletedTask;
- }
- }
-
private MatcherEndpoint CreateEndpoint(
ActionDescriptor action,
string routeName,
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/NullRouter.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/NullRouter.cs
new file mode 100644
index 0000000000..c1b800c5bd
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/NullRouter.cs
@@ -0,0 +1,27 @@
+// 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.Threading.Tasks;
+using Microsoft.AspNetCore.Routing;
+
+namespace Microsoft.AspNetCore.Mvc.Internal
+{
+ internal class NullRouter : IRouter
+ {
+ public static IRouter Instance = new NullRouter();
+
+ private NullRouter()
+ {
+ }
+
+ public VirtualPathData GetVirtualPath(VirtualPathContext context)
+ {
+ return null;
+ }
+
+ public Task RouteAsync(RouteContext context)
+ {
+ return Task.CompletedTask;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/MvcOptions.cs b/src/Microsoft.AspNetCore.Mvc.Core/MvcOptions.cs
index 72634a49fa..e87ddb2678 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/MvcOptions.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/MvcOptions.cs
@@ -30,6 +30,7 @@ namespace Microsoft.AspNetCore.Mvc
private readonly CompatibilitySwitch _allowValidatingTopLevelNodes;
private readonly CompatibilitySwitch _inputFormatterExceptionPolicy;
private readonly CompatibilitySwitch _suppressBindingUndefinedValueToEnumType;
+ private readonly CompatibilitySwitch _enableGlobalRouting;
private readonly ICompatibilitySwitch[] _switches;
///
@@ -54,6 +55,7 @@ namespace Microsoft.AspNetCore.Mvc
_allowValidatingTopLevelNodes = new CompatibilitySwitch(nameof(AllowValidatingTopLevelNodes));
_inputFormatterExceptionPolicy = new CompatibilitySwitch(nameof(InputFormatterExceptionPolicy), InputFormatterExceptionPolicy.AllExceptions);
_suppressBindingUndefinedValueToEnumType = new CompatibilitySwitch(nameof(SuppressBindingUndefinedValueToEnumType));
+ _enableGlobalRouting = new CompatibilitySwitch(nameof(EnableGlobalRouting));
_switches = new ICompatibilitySwitch[]
{
@@ -62,9 +64,17 @@ namespace Microsoft.AspNetCore.Mvc
_allowValidatingTopLevelNodes,
_inputFormatterExceptionPolicy,
_suppressBindingUndefinedValueToEnumType,
+ _enableGlobalRouting,
};
}
+ // REVIEW: Add documentation
+ public bool EnableGlobalRouting
+ {
+ get => _enableGlobalRouting.Value;
+ set => _enableGlobalRouting.Value = value;
+ }
+
///
/// Gets or sets the flag which decides whether body model binding (for example, on an
/// action method parameter with ) should treat empty
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/GlobalRoutingUrlHelperTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/GlobalRoutingUrlHelperTest.cs
index 78c7551c43..b207c8dda4 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/GlobalRoutingUrlHelperTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/GlobalRoutingUrlHelperTest.cs
@@ -16,6 +16,83 @@ namespace Microsoft.AspNetCore.Mvc.Routing
{
public class GlobalRoutingUrlHelperTest : UrlHelperTestBase
{
+ [Fact]
+ public void RouteUrl_WithRouteName_GeneratesUrl_UsingDefaults()
+ {
+ // Arrange
+ var endpoint1 = CreateEndpoint(
+ "api/orders/{id}",
+ defaults: new { controller = "Orders", action = "GetById" },
+ requiredValues: new { controller = "Orders", action = "GetById" },
+ routeName: "OrdersApi");
+ var endpoint2 = CreateEndpoint(
+ "api/orders",
+ defaults: new { controller = "Orders", action = "GetAll" },
+ requiredValues: new { controller = "Orders", action = "GetAll" },
+ routeName: "OrdersApi");
+ var urlHelper = CreateUrlHelper(new[] { endpoint1, endpoint2 });
+
+ // Act
+ var url = urlHelper.RouteUrl(
+ routeName: "OrdersApi",
+ values: new { });
+
+ // Assert
+ Assert.Equal("/" + endpoint2.RoutePattern.RawText, url);
+ }
+
+ [Fact]
+ public void RouteUrl_WithRouteName_UsesAmbientValues()
+ {
+ // Arrange
+ var endpoint1 = CreateEndpoint(
+ "api/orders/{id}",
+ defaults: new { controller = "Orders", action = "GetById" },
+ requiredValues: new { controller = "Orders", action = "GetById" },
+ routeName: "OrdersApi");
+ var endpoint2 = CreateEndpoint(
+ "api/orders",
+ defaults: new { controller = "Orders", action = "GetAll" },
+ requiredValues: new { controller = "Orders", action = "GetAll" },
+ routeName: "OrdersApi");
+ var urlHelper = CreateUrlHelper(new[] { endpoint1, endpoint2 });
+ urlHelper.ActionContext.RouteData.Values["id"] = "500";
+
+ // Act
+ var url = urlHelper.RouteUrl(
+ routeName: "OrdersApi",
+ values: new { });
+
+ // Assert
+ Assert.Equal("/api/orders/500", url);
+ }
+
+ [Fact]
+ public void RouteUrl_WithRouteName_UsesSuppliedValue_OverridingAmbientValue()
+ {
+ // Arrange
+ var endpoint1 = CreateEndpoint(
+ "api/orders/{id}",
+ defaults: new { controller = "Orders", action = "GetById" },
+ requiredValues: new { controller = "Orders", action = "GetById" },
+ routeName: "OrdersApi");
+ var endpoint2 = CreateEndpoint(
+ "api/orders",
+ defaults: new { controller = "Orders", action = "GetAll" },
+ requiredValues: new { controller = "Orders", action = "GetAll" },
+ routeName: "OrdersApi");
+ var urlHelper = CreateUrlHelper(new[] { endpoint1, endpoint2 });
+ urlHelper.ActionContext.RouteData.Values["id"] = "500";
+
+ // Act
+ var url = urlHelper.RouteUrl(
+ routeName: "OrdersApi",
+ values: new { id = "10" });
+
+ // Assert
+ Assert.Equal("/api/orders/10", url);
+ }
+
protected override IUrlHelper CreateUrlHelper(string appRoot, string host, string protocol)
{
return CreateUrlHelper(Enumerable.Empty(), appRoot, host, protocol);
@@ -84,6 +161,14 @@ namespace Microsoft.AspNetCore.Mvc.Routing
return CreateUrlHelper(actionContext);
}
+ private IUrlHelper CreateUrlHelper(IEnumerable endpoints, ActionContext actionContext = null)
+ {
+ var serviceProvider = CreateServices(endpoints);
+ var httpContext = CreateHttpContext(serviceProvider, null, null, "http");
+ actionContext = actionContext ?? CreateActionContext(httpContext);
+ return CreateUrlHelper(actionContext);
+ }
+
private IUrlHelper CreateUrlHelper(
IEnumerable endpoints,
string appRoot,
@@ -99,33 +184,98 @@ namespace Microsoft.AspNetCore.Mvc.Routing
private List GetDefaultEndpoints()
{
var endpoints = new List();
- endpoints.Add(CreateEndpoint(null, "home/newaction/{id}", new { id = "defaultid", controller = "home", action = "newaction" }, 1));
- endpoints.Add(CreateEndpoint(null, "home/contact/{id}", new { id = "defaultid", controller = "home", action = "contact" }, 2));
- endpoints.Add(CreateEndpoint(null, "home2/newaction/{id}", new { id = "defaultid", controller = "home2", action = "newaction" }, 3));
- endpoints.Add(CreateEndpoint(null, "home2/contact/{id}", new { id = "defaultid", controller = "home2", action = "contact" }, 4));
- endpoints.Add(CreateEndpoint(null, "home3/contact/{id}", new { id = "defaultid", controller = "home3", action = "contact" }, 5));
- endpoints.Add(CreateEndpoint("namedroute", "named/home/newaction/{id}", new { id = "defaultid", controller = "home", action = "newaction" }, 6));
- endpoints.Add(CreateEndpoint("namedroute", "named/home2/newaction/{id}", new { id = "defaultid", controller = "home2", action = "newaction" }, 7));
- endpoints.Add(CreateEndpoint("namedroute", "named/home/contact/{id}", new { id = "defaultid", controller = "home", action = "contact" }, 8));
- endpoints.Add(CreateEndpoint("MyRouteName", "any/url", new { }, 9));
+ endpoints.Add(
+ CreateEndpoint(
+ "home/newaction/{id}",
+ defaults: new { id = "defaultid", controller = "home", action = "newaction" },
+ requiredValues: new { controller = "home", action = "newaction" },
+ order: 1));
+ endpoints.Add(
+ CreateEndpoint(
+ "home/contact/{id}",
+ defaults: new { id = "defaultid", controller = "home", action = "contact" },
+ requiredValues: new { controller = "home", action = "contact" },
+ order: 2));
+ endpoints.Add(
+ CreateEndpoint(
+ "home2/newaction/{id}",
+ defaults: new { id = "defaultid", controller = "home2", action = "newaction" },
+ requiredValues: new { controller = "home2", action = "newaction" },
+ order: 3));
+ endpoints.Add(
+ CreateEndpoint(
+ "home2/contact/{id}",
+ defaults: new { id = "defaultid", controller = "home2", action = "contact" },
+ requiredValues: new { controller = "home2", action = "contact" },
+ order: 4));
+ endpoints.Add(
+ CreateEndpoint(
+ "home3/contact/{id}",
+ defaults: new { id = "defaultid", controller = "home3", action = "contact" },
+ requiredValues: new { controller = "home3", action = "contact" },
+ order: 5));
+ endpoints.Add(
+ CreateEndpoint(
+ "named/home/newaction/{id}",
+ defaults: new { id = "defaultid", controller = "home", action = "newaction" },
+ requiredValues: new { controller = "home", action = "newaction" },
+ order: 6,
+ routeName: "namedroute"));
+ endpoints.Add(
+ CreateEndpoint(
+ "named/home2/newaction/{id}",
+ defaults: new { id = "defaultid", controller = "home2", action = "newaction" },
+ requiredValues: new { controller = "home2", action = "newaction" },
+ order: 7,
+ routeName: "namedroute"));
+ endpoints.Add(
+ CreateEndpoint(
+ "named/home/contact/{id}",
+ defaults: new { id = "defaultid", controller = "home", action = "contact" },
+ requiredValues: new { controller = "home", action = "contact" },
+ order: 8,
+ routeName: "namedroute"));
+ endpoints.Add(
+ CreateEndpoint(
+ "any/url",
+ defaults: new { },
+ requiredValues: new { },
+ order: 9,
+ routeName: "MyRouteName"));
+ endpoints.Add(
+ CreateEndpoint(
+ "api/orders/{id}",
+ defaults: new { controller = "Orders", action = "GetById" },
+ requiredValues: new { controller = "Orders", action = "GetById" },
+ order: 10,
+ routeName: "OrdersApi"));
return endpoints;
}
- private MatcherEndpoint CreateEndpoint(string routeName, string template, object defaults, int order)
+ private MatcherEndpoint CreateEndpoint(
+ string template,
+ object defaults = null,
+ object requiredValues = null,
+ int order = 0,
+ string routeName = null,
+ EndpointMetadataCollection metadataCollection = null)
{
- var metadata = EndpointMetadataCollection.Empty;
- if (!string.IsNullOrEmpty(routeName))
+ if (metadataCollection == null)
{
- metadata = new EndpointMetadataCollection(new[] { new RouteNameMetadata(routeName) });
+ metadataCollection = EndpointMetadataCollection.Empty;
+ if (!string.IsNullOrEmpty(routeName))
+ {
+ metadataCollection = new EndpointMetadataCollection(new[] { new RouteNameMetadata(routeName) });
+ }
}
return new MatcherEndpoint(
next => (httpContext) => Task.CompletedTask,
RoutePatternFactory.Parse(template, defaults, constraints: null),
- new RouteValueDictionary(),
+ new RouteValueDictionary(requiredValues),
order,
- metadata,
- "DisplayName");
+ metadataCollection,
+ null);
}
private IServiceProvider CreateServices(IEnumerable endpoints)
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperTest.cs
index 63c88896ab..2d7e4a0f15 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperTest.cs
@@ -101,6 +101,11 @@ namespace Microsoft.AspNetCore.Mvc.Routing
.Returns(context => null);
routeBuilder.DefaultHandler = target.Object;
+ routeBuilder.MapRoute(
+ "OrdersApi",
+ "api/orders/{id}",
+ new RouteValueDictionary(new { controller = "Orders", action = "GetById" }));
+
routeBuilder.MapRoute(
string.Empty,
"{controller}/{action}/{id}",
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperTestBase.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperTestBase.cs
index 300efe8c4a..d6dfe08822 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperTestBase.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Routing/UrlHelperTestBase.cs
@@ -457,6 +457,36 @@ namespace Microsoft.AspNetCore.Mvc.Routing
Assert.Equal("https://pingüino/app/named/home2/newaction/someid", url);
}
+ [Fact]
+ public void RouteUrl_GeneratesUrl_WithRouteName_UsingDefaultValues_WhenExplicitOrAmbientValues_NotPresent()
+ {
+ // Arrange
+ var urlHelper = CreateUrlHelperWithDefaultRoutes();
+
+ // Act
+ var url = urlHelper.RouteUrl(
+ routeName: "OrdersApi",
+ values: new { id = "500" });
+
+ // Assert
+ Assert.Equal("/app/api/orders/500", url);
+ }
+
+ [Fact]
+ public void RouteUrl_WithRouteName_DoesNotGenerateUrl_WhenRequiredValueForParameter_NotPresent()
+ {
+ // Arrange
+ var urlHelper = CreateUrlHelperWithDefaultRoutes();
+
+ // Act
+ var url = urlHelper.RouteUrl(
+ routeName: "OrdersApi",
+ values: new { });
+
+ // Assert
+ Assert.Null(url);
+ }
+
[Fact]
public void RouteUrlWithRouteNameAndDictionary()
{
@@ -922,7 +952,6 @@ namespace Microsoft.AspNetCore.Mvc.Routing
Assert.Equal("/b/Store/Checkout", url);
}
-
protected abstract IServiceProvider CreateServices();
protected abstract IUrlHelper CreateUrlHelper(ActionContext actionContext);
diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ConsumesAttributeGlobalRoutingTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ConsumesAttributeGlobalRoutingTests.cs
index 889e77e80b..1683864c17 100644
--- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ConsumesAttributeGlobalRoutingTests.cs
+++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ConsumesAttributeGlobalRoutingTests.cs
@@ -1,6 +1,11 @@
// 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.Net;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using Xunit;
+
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class ConsumesAttributeGlobalRoutingTests : ConsumesAttributeTestsBase
@@ -9,5 +14,20 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
: base(fixture)
{
}
+
+ [Fact]
+ public async override Task HasEndpointMatch()
+ {
+ // Arrange & Act
+ var response = await Client.GetAsync("http://localhost/Routing/HasEndpointMatch");
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+
+ var body = await response.Content.ReadAsStringAsync();
+ var result = JsonConvert.DeserializeObject(body);
+
+ Assert.True(result);
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ConsumesAttributeTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ConsumesAttributeTests.cs
index a8db21d806..d694aad1c4 100644
--- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ConsumesAttributeTests.cs
+++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ConsumesAttributeTests.cs
@@ -1,6 +1,11 @@
// 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.Net;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using Xunit;
+
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class ConsumesAttributeTests : ConsumesAttributeTestsBase
@@ -9,5 +14,20 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
: base(fixture)
{
}
+
+ [Fact]
+ public async override Task HasEndpointMatch()
+ {
+ // Arrange & Act
+ var response = await Client.GetAsync("http://localhost/Routing/HasEndpointMatch");
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+
+ var body = await response.Content.ReadAsStringAsync();
+ var result = JsonConvert.DeserializeObject(body);
+
+ Assert.False(result);
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ConsumesAttributeTestsBase.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ConsumesAttributeTestsBase.cs
index 9a414488b1..acc68febbc 100644
--- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ConsumesAttributeTestsBase.cs
+++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/ConsumesAttributeTestsBase.cs
@@ -28,6 +28,9 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public HttpClient Client { get; }
+ [Fact]
+ public abstract Task HasEndpointMatch();
+
[Fact]
public async Task NoRequestContentType_SelectsActionWithoutConstraint()
{
diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/GlobalRoutingTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/GlobalRoutingTest.cs
index 1ab9950d68..574b7f8160 100644
--- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/GlobalRoutingTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/GlobalRoutingTest.cs
@@ -3,6 +3,7 @@
using System;
using System.Net;
+using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Xunit;
@@ -16,6 +17,21 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
}
+ [Fact]
+ public async override Task HasEndpointMatch()
+ {
+ // Arrange & Act
+ var response = await Client.GetAsync("http://localhost/Routing/HasEndpointMatch");
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+
+ var body = await response.Content.ReadAsStringAsync();
+ var result = JsonConvert.DeserializeObject(body);
+
+ Assert.True(result);
+ }
+
[Fact(Skip = "Link generation issue in global routing. Need to fix - https://github.com/aspnet/Routing/issues/590")]
public override Task AttributeRoutedAction_InArea_StaysInArea_ActionDoesntExist()
{
@@ -61,5 +77,88 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
Array.Empty(),
result.Routers);
}
+
+ // Global routing exposes HTTP 405s for HTTP method mismatches
+ [Fact]
+ public override async Task ConventionalRoutedController_InArea_ActionBlockedByHttpMethod()
+ {
+ // Arrange & Act
+ var response = await Client.GetAsync("http://localhost/Travel/Flight/BuyTickets");
+
+ // Assert
+ Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode);
+ }
+
+ // Global routing exposes HTTP 405s for HTTP method mismatches
+ [Fact]
+ public override async Task AttributeRoutedAction_MultipleRouteAttributes_RouteAttributeTemplatesIgnoredForOverrideActions()
+ {
+ // Arrange
+ var url = "http://localhost/api/v1/Maps";
+
+ // Act
+ var response = await Client.SendAsync(new HttpRequestMessage(new HttpMethod("POST"), url));
+
+ // Assert
+ Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode);
+ }
+
+ // Global routing exposes HTTP 405s for HTTP method mismatches
+ [Theory]
+ [InlineData("http://localhost/api/v1/Maps/5", "PATCH")]
+ [InlineData("http://localhost/api/v2/Maps/5", "PATCH")]
+ [InlineData("http://localhost/api/v1/Maps/PartialUpdate/5", "PUT")]
+ [InlineData("http://localhost/api/v2/Maps/PartialUpdate/5", "PUT")]
+ public override async Task AttributeRoutedAction_MultipleRouteAttributes_WithMultipleHttpAttributes_RespectsConstraints(
+ string url,
+ string method)
+ {
+ // Arrange
+ var expectedUrl = new Uri(url).AbsolutePath;
+
+ // Act
+ var response = await Client.SendAsync(new HttpRequestMessage(new HttpMethod(method), url));
+
+ // Assert
+ Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode);
+ }
+
+ // Global routing exposes HTTP 405s for HTTP method mismatches
+ [Theory]
+ [InlineData("Post", "/Friends")]
+ [InlineData("Put", "/Friends")]
+ [InlineData("Patch", "/Friends")]
+ [InlineData("Options", "/Friends")]
+ [InlineData("Head", "/Friends")]
+ public override async Task AttributeRoutedAction_RejectsRequestsWithWrongMethods_InRoutesWithoutExtraTemplateSegmentsOnTheAction(
+ string method,
+ string url)
+ {
+ // Arrange
+ var request = new HttpRequestMessage(new HttpMethod(method), $"http://localhost{url}");
+
+ // Assert
+ var response = await Client.SendAsync(request);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode);
+ }
+
+ // These verbs don't match
+ [Theory]
+ [InlineData("/Bank/Deposit", "GET")]
+ [InlineData("/Bank/Deposit/5", "DELETE")]
+ [InlineData("/Bank/Withdraw/5", "GET")]
+ public override async Task AttributeRouting_MixedAcceptVerbsAndRoute_Unreachable(string path, string verb)
+ {
+ // Arrange
+ var request = new HttpRequestMessage(new HttpMethod(verb), "http://localhost" + path);
+
+ // Act
+ var response = await Client.SendAsync(request);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode);
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RequestServicesGlobalRoutingTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RequestServicesGlobalRoutingTest.cs
index 0c1471df03..0ca18c250e 100644
--- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RequestServicesGlobalRoutingTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RequestServicesGlobalRoutingTest.cs
@@ -1,6 +1,11 @@
// 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.Net;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using Xunit;
+
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class RequestServicesGlobalRoutingTest : RequestServicesTestBase
@@ -9,5 +14,20 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
: base(fixture)
{
}
+
+ [Fact]
+ public async override Task HasEndpointMatch()
+ {
+ // Arrange & Act
+ var response = await Client.GetAsync("http://localhost/Routing/HasEndpointMatch");
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+
+ var body = await response.Content.ReadAsStringAsync();
+ var result = JsonConvert.DeserializeObject(body);
+
+ Assert.True(result);
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RequestServicesTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RequestServicesTest.cs
index f62521bd6c..73c0e5af15 100644
--- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RequestServicesTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RequestServicesTest.cs
@@ -1,6 +1,11 @@
// 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.Net;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using Xunit;
+
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class RequestServicesTest : RequestServicesTestBase
@@ -9,5 +14,20 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
: base(fixture)
{
}
+
+ [Fact]
+ public async override Task HasEndpointMatch()
+ {
+ // Arrange & Act
+ var response = await Client.GetAsync("http://localhost/Routing/HasEndpointMatch");
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+
+ var body = await response.Content.ReadAsStringAsync();
+ var result = JsonConvert.DeserializeObject(body);
+
+ Assert.False(result);
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RequestServicesTestBase.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RequestServicesTestBase.cs
index 425bda5ea5..cc4e06ba74 100644
--- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RequestServicesTestBase.cs
+++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RequestServicesTestBase.cs
@@ -26,6 +26,9 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public HttpClient Client { get; }
+ [Fact]
+ public abstract Task HasEndpointMatch();
+
[Theory]
[InlineData("http://localhost/RequestScopedService/FromFilter")]
[InlineData("http://localhost/RequestScopedService/FromView")]
diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTests.cs
index e4c3f088ea..a4fbb4ab58 100644
--- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTests.cs
+++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTests.cs
@@ -17,6 +17,21 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
}
+ [Fact]
+ public async override Task HasEndpointMatch()
+ {
+ // Arrange & Act
+ var response = await Client.GetAsync("http://localhost/Routing/HasEndpointMatch");
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+
+ var body = await response.Content.ReadAsStringAsync();
+ var result = JsonConvert.DeserializeObject(body);
+
+ Assert.False(result);
+ }
+
[Fact]
public async override Task RouteData_Routers_ConventionalRoute()
{
diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTestsBase.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTestsBase.cs
index 20918254b9..a3d236d318 100644
--- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTestsBase.cs
+++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RoutingTestsBase.cs
@@ -28,6 +28,9 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public HttpClient Client { get; }
+ [Fact]
+ public abstract Task HasEndpointMatch();
+
[Fact]
public abstract Task RouteData_Routers_ConventionalRoute();
@@ -251,7 +254,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
[InlineData("Patch", "/Friends")]
[InlineData("Options", "/Friends")]
[InlineData("Head", "/Friends")]
- public async Task AttributeRoutedAction_RejectsRequestsWithWrongMethods_InRoutesWithoutExtraTemplateSegmentsOnTheAction(
+ public virtual async Task AttributeRoutedAction_RejectsRequestsWithWrongMethods_InRoutesWithoutExtraTemplateSegmentsOnTheAction(
string method,
string url)
{
@@ -318,7 +321,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
}
[Fact]
- public async Task AttributeRoutedAction_MultipleRouteAttributes_RouteAttributeTemplatesIgnoredForOverrideActions()
+ public virtual async Task AttributeRoutedAction_MultipleRouteAttributes_RouteAttributeTemplatesIgnoredForOverrideActions()
{
// Arrange
var url = "http://localhost/api/v1/Maps";
@@ -392,7 +395,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
[InlineData("http://localhost/api/v2/Maps/5", "PATCH")]
[InlineData("http://localhost/api/v1/Maps/PartialUpdate/5", "PUT")]
[InlineData("http://localhost/api/v2/Maps/PartialUpdate/5", "PUT")]
- public async Task AttributeRoutedAction_MultipleRouteAttributes_WithMultipleHttpAttributes_RespectsConstraints(
+ public virtual async Task AttributeRoutedAction_MultipleRouteAttributes_WithMultipleHttpAttributes_RespectsConstraints(
string url,
string method)
{
@@ -1209,7 +1212,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
[InlineData("/Bank/Deposit", "GET")]
[InlineData("/Bank/Deposit/5", "DELETE")]
[InlineData("/Bank/Withdraw/5", "GET")]
- public async Task AttributeRouting_MixedAcceptVerbsAndRoute_Unreachable(string path, string verb)
+ public virtual async Task AttributeRouting_MixedAcceptVerbsAndRoute_Unreachable(string path, string verb)
{
// Arrange
var request = new HttpRequestMessage(new HttpMethod(verb), "http://localhost" + path);
diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningGlobalRoutingTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningGlobalRoutingTests.cs
index 8c022a076c..af2147949f 100644
--- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningGlobalRoutingTests.cs
+++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningGlobalRoutingTests.cs
@@ -1,6 +1,11 @@
// 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.Net;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using Xunit;
+
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class VersioningGlobalRoutingTests : VersioningTestsBase
@@ -9,5 +14,20 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
: base(fixture)
{
}
+
+ [Fact]
+ public async override Task HasEndpointMatch()
+ {
+ // Arrange & Act
+ var response = await Client.GetAsync("http://localhost/Routing/HasEndpointMatch");
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+
+ var body = await response.Content.ReadAsStringAsync();
+ var result = JsonConvert.DeserializeObject(body);
+
+ Assert.True(result);
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningTests.cs
index 8490fda117..40e14bdc57 100644
--- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningTests.cs
+++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningTests.cs
@@ -1,6 +1,11 @@
// 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.Net;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using Xunit;
+
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class VersioningTests : VersioningTestsBase
@@ -9,5 +14,20 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
: base(fixture)
{
}
+
+ [Fact]
+ public async override Task HasEndpointMatch()
+ {
+ // Arrange & Act
+ var response = await Client.GetAsync("http://localhost/Routing/HasEndpointMatch");
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+
+ var body = await response.Content.ReadAsStringAsync();
+ var result = JsonConvert.DeserializeObject(body);
+
+ Assert.False(result);
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningTestsBase.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningTestsBase.cs
index 46cfdbe4a8..152c08a799 100644
--- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningTestsBase.cs
+++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/VersioningTestsBase.cs
@@ -25,6 +25,9 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public HttpClient Client { get; }
+ [Fact]
+ public abstract Task HasEndpointMatch();
+
[Theory]
[InlineData("1")]
[InlineData("2")]
diff --git a/test/Microsoft.AspNetCore.Mvc.Test/IntegrationTest/CompatibilitySwitchIntegrationTest.cs b/test/Microsoft.AspNetCore.Mvc.Test/IntegrationTest/CompatibilitySwitchIntegrationTest.cs
index 998343d7bb..78f8ddb4ae 100644
--- a/test/Microsoft.AspNetCore.Mvc.Test/IntegrationTest/CompatibilitySwitchIntegrationTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Test/IntegrationTest/CompatibilitySwitchIntegrationTest.cs
@@ -39,6 +39,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest
Assert.Equal(InputFormatterExceptionPolicy.AllExceptions, mvcOptions.InputFormatterExceptionPolicy);
Assert.False(jsonOptions.AllowInputFormatterExceptionMessages);
Assert.False(razorPagesOptions.AllowAreas);
+ Assert.False(mvcOptions.EnableGlobalRouting);
}
[Fact]
@@ -63,6 +64,32 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest
Assert.Equal(InputFormatterExceptionPolicy.MalformedInputExceptions, mvcOptions.InputFormatterExceptionPolicy);
Assert.True(jsonOptions.AllowInputFormatterExceptionMessages);
Assert.True(razorPagesOptions.AllowAreas);
+ Assert.False(mvcOptions.EnableGlobalRouting);
+ }
+
+ [Fact]
+ public void CompatibilitySwitches_Version_2_2()
+ {
+ // Arrange
+ var serviceCollection = new ServiceCollection();
+ AddHostingServices(serviceCollection);
+ serviceCollection.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
+
+ var services = serviceCollection.BuildServiceProvider();
+
+ // Act
+ var mvcOptions = services.GetRequiredService>().Value;
+ var jsonOptions = services.GetRequiredService>().Value;
+ var razorPagesOptions = services.GetRequiredService>().Value;
+
+ // Assert
+ Assert.True(mvcOptions.AllowCombiningAuthorizeFilters);
+ Assert.True(mvcOptions.AllowBindingHeaderValuesToNonStringModelTypes);
+ Assert.True(mvcOptions.SuppressBindingUndefinedValueToEnumType);
+ Assert.Equal(InputFormatterExceptionPolicy.MalformedInputExceptions, mvcOptions.InputFormatterExceptionPolicy);
+ Assert.True(jsonOptions.AllowInputFormatterExceptionMessages);
+ Assert.True(razorPagesOptions.AllowAreas);
+ Assert.True(mvcOptions.EnableGlobalRouting);
}
[Fact]
@@ -87,6 +114,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest
Assert.Equal(InputFormatterExceptionPolicy.MalformedInputExceptions, mvcOptions.InputFormatterExceptionPolicy);
Assert.True(jsonOptions.AllowInputFormatterExceptionMessages);
Assert.True(razorPagesOptions.AllowAreas);
+ Assert.True(mvcOptions.EnableGlobalRouting);
}
// This just does the minimum needed to be able to resolve these options.
diff --git a/test/WebSites/BasicWebSite/Controllers/RoutingController.cs b/test/WebSites/BasicWebSite/Controllers/RoutingController.cs
new file mode 100644
index 0000000000..afe914f572
--- /dev/null
+++ b/test/WebSites/BasicWebSite/Controllers/RoutingController.cs
@@ -0,0 +1,17 @@
+// 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.Mvc;
+using Microsoft.AspNetCore.Routing;
+
+namespace BasicWebSite
+{
+ public class RoutingController : Controller
+ {
+ public ActionResult HasEndpointMatch()
+ {
+ var endpointFeature = HttpContext.Features.Get();
+ return Json(endpointFeature?.Endpoint != null);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/WebSites/BasicWebSite/Startup.cs b/test/WebSites/BasicWebSite/Startup.cs
index 9e20cb447c..c6e406fb34 100644
--- a/test/WebSites/BasicWebSite/Startup.cs
+++ b/test/WebSites/BasicWebSite/Startup.cs
@@ -26,6 +26,9 @@ namespace BasicWebSite
options.Conventions.Add(new ApplicationDescription("This is a basic website."));
// Filter that records a value in HttpContext.Items
options.Filters.Add(new TraceResourceFilter());
+
+ // Remove when all URL generation tests are passing - https://github.com/aspnet/Routing/issues/590
+ options.EnableGlobalRouting = false;
})
.SetCompatibilityVersion(CompatibilityVersion.Latest)
.AddXmlDataContractSerializerFormatters();
diff --git a/test/WebSites/BasicWebSite/StartupWithGlobalRouting.cs b/test/WebSites/BasicWebSite/StartupWithGlobalRouting.cs
index 037a5312f8..d3a540a86c 100644
--- a/test/WebSites/BasicWebSite/StartupWithGlobalRouting.cs
+++ b/test/WebSites/BasicWebSite/StartupWithGlobalRouting.cs
@@ -15,7 +15,7 @@ namespace BasicWebSite
services.AddRouting();
services.AddMvc()
- .SetCompatibilityVersion(CompatibilityVersion.Latest)
+ .SetCompatibilityVersion(CompatibilityVersion.Latest) // this compat version enables global routing
.AddXmlDataContractSerializerFormatters();
services.ConfigureBaseWebSiteAuthPolicies();
@@ -31,9 +31,9 @@ namespace BasicWebSite
app.UseGlobalRouting();
- app.UseMvcWithEndpoint(routes =>
+ app.UseMvc(routes =>
{
- routes.MapEndpoint(
+ routes.MapRoute(
"ActionAsMethod",
"{controller}/{action}",
defaults: new { controller = "Home", action = "Index" });
diff --git a/test/WebSites/RoutingWebSite/Controllers/RoutingController.cs b/test/WebSites/RoutingWebSite/Controllers/RoutingController.cs
new file mode 100644
index 0000000000..354ff5539a
--- /dev/null
+++ b/test/WebSites/RoutingWebSite/Controllers/RoutingController.cs
@@ -0,0 +1,17 @@
+// 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.Mvc;
+using Microsoft.AspNetCore.Routing;
+
+namespace RoutingWebSite
+{
+ public class RoutingController : Controller
+ {
+ public ActionResult HasEndpointMatch()
+ {
+ var endpointFeature = HttpContext.Features.Get();
+ return Json(endpointFeature?.Endpoint != null);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/WebSites/RoutingWebSite/StartupWithGlobalRouting.cs b/test/WebSites/RoutingWebSite/StartupWithGlobalRouting.cs
index abc0abf4fb..62a84f5ecb 100644
--- a/test/WebSites/RoutingWebSite/StartupWithGlobalRouting.cs
+++ b/test/WebSites/RoutingWebSite/StartupWithGlobalRouting.cs
@@ -12,9 +12,8 @@ namespace RoutingWebSite
// Set up application services
public void ConfigureServices(IServiceCollection services)
{
- services.AddRouting();
-
- services.AddMvc();
+ services.AddMvc()
+ .AddMvcOptions(options => options.EnableGlobalRouting = true);
services.AddScoped();
services.AddSingleton();
@@ -22,23 +21,21 @@ namespace RoutingWebSite
public void Configure(IApplicationBuilder app)
{
- app.UseGlobalRouting();
-
- app.UseMvcWithEndpoint(routes =>
+ app.UseMvc(routes =>
{
- routes.MapAreaEndpoint(
+ routes.MapAreaRoute(
"flightRoute",
"adminRoute",
"{area:exists}/{controller}/{action}",
new { controller = "Home", action = "Index" },
new { area = "Travel" });
- routes.MapEndpoint(
+ routes.MapRoute(
"ActionAsMethod",
"{controller}/{action}",
defaults: new { controller = "Home", action = "Index" });
- routes.MapEndpoint(
+ routes.MapRoute(
"RouteWithOptionalSegment",
"{controller}/{action}/{path?}");
});
diff --git a/test/WebSites/VersioningWebSite/Controllers/RoutingController.cs b/test/WebSites/VersioningWebSite/Controllers/RoutingController.cs
new file mode 100644
index 0000000000..aad2099d1d
--- /dev/null
+++ b/test/WebSites/VersioningWebSite/Controllers/RoutingController.cs
@@ -0,0 +1,17 @@
+// 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.Mvc;
+using Microsoft.AspNetCore.Routing;
+
+namespace VersioningWebSite
+{
+ public class RoutingController : Controller
+ {
+ public ActionResult HasEndpointMatch()
+ {
+ var endpointFeature = HttpContext.Features.Get();
+ return Json(endpointFeature?.Endpoint != null);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/WebSites/VersioningWebSite/StartupWithGlobalRouting.cs b/test/WebSites/VersioningWebSite/StartupWithGlobalRouting.cs
index c7ea9739f7..249aefabd4 100644
--- a/test/WebSites/VersioningWebSite/StartupWithGlobalRouting.cs
+++ b/test/WebSites/VersioningWebSite/StartupWithGlobalRouting.cs
@@ -13,10 +13,9 @@ namespace VersioningWebSite
{
public void ConfigureServices(IServiceCollection services)
{
- services.AddRouting();
-
// Add MVC services to the services container
- services.AddMvc();
+ services.AddMvc()
+ .AddMvcOptions(options => options.EnableGlobalRouting = true);
services.AddScoped();
services.AddSingleton();
@@ -24,14 +23,7 @@ namespace VersioningWebSite
public void Configure(IApplicationBuilder app)
{
- app.UseGlobalRouting();
-
- app.UseMvcWithEndpoint(endpoints =>
- {
- endpoints.MapEndpoint(
- name: "default",
- template: "{controller=Home}/{action=Index}/{id?}");
- });
+ app.UseMvcWithDefaultRoute();
}
}
}
\ No newline at end of file