Make UseEndpoints a thing (#8765)

* Make endpoint middleware explicit

This change makes the endpoint middleware explicit again, and updates
all of the templates.

The other change here is make UseEndpoints be the place where you
register endpoints. This is vital because it puts your code visually at
the point of the pipeline where it executes.

Lastly, I removed support for UseMvc with endpoint routing. This is
causing issues for some security features, and we're moving in the
direction of trying to make the middleware heavy implementation required
in 3.0. There are some issues we won't be able to fix in MVC if we can't
unambiguously know if UseMvc was used or the middleware.
This commit is contained in:
Ryan Nowak 2019-03-28 15:41:11 -07:00 committed by GitHub
parent 062cfac384
commit 658b37d2bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
137 changed files with 1370 additions and 1303 deletions

View File

@ -38,9 +38,18 @@ namespace AzureADB2CSample
public void Configure(IApplicationBuilder app)
{
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}
}
}

View File

@ -38,9 +38,18 @@ namespace AzureADSample
public void Configure(IApplicationBuilder app)
{
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}
}
}

View File

@ -16,9 +16,16 @@ namespace AzureAD.WebSite
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseRouting();
app.UseMvc();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}
}
}

View File

@ -32,9 +32,11 @@ namespace BlazorHosted_CSharp.Server
app.UseBlazorDebugging();
}
app.UseMvc(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapRoute(name: "default", template: "{controller}/{action}/{id?}");
endpoints.MapDefaultControllerRoute();
});
app.UseBlazor<Client.Startup>();

View File

@ -17,16 +17,16 @@ namespace Microsoft.AspNetCore.Builder
/// the component <typeparamref name="TComponent"/> to this hub instance as the given DOM <paramref name="selector"/>.
/// </summary>
/// <typeparam name="TComponent">The first <see cref="IComponent"/> associated with this <see cref="ComponentHub"/>.</typeparam>
/// <param name="routes">The <see cref="RouteBuilder"/>.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/>.</param>
/// <param name="selector">The selector for the <typeparamref name="TComponent"/>.</param>
/// <returns>The <see cref="IEndpointConventionBuilder"/>.</returns>
public static IEndpointConventionBuilder MapComponentHub<TComponent>(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string selector)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (selector == null)
@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Builder
throw new ArgumentNullException(nameof(selector));
}
return routes.MapComponentHub(typeof(TComponent), selector, ComponentHub.DefaultPath);
return endpoints.MapComponentHub(typeof(TComponent), selector, ComponentHub.DefaultPath);
}
/// <summary>
@ -42,18 +42,18 @@ namespace Microsoft.AspNetCore.Builder
/// the component <typeparamref name="TComponent"/> to this hub instance as the given DOM <paramref name="selector"/>.
/// </summary>
/// <typeparam name="TComponent">The first <see cref="IComponent"/> associated with this <see cref="ComponentHub"/>.</typeparam>
/// <param name="routes">The <see cref="RouteBuilder"/>.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/>.</param>
/// <param name="selector">The selector for the <typeparamref name="TComponent"/>.</param>
/// <param name="path">The path to map to which the <see cref="ComponentHub"/> will be mapped.</param>
/// <returns>The <see cref="IEndpointConventionBuilder"/>.</returns>
public static IEndpointConventionBuilder MapComponentHub<TComponent>(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string selector,
string path)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (path == null)
@ -66,27 +66,27 @@ namespace Microsoft.AspNetCore.Builder
throw new ArgumentNullException(nameof(selector));
}
return routes.MapComponentHub(typeof(TComponent), selector, path);
return endpoints.MapComponentHub(typeof(TComponent), selector, path);
}
/// <summary>
/// Maps the SignalR <see cref="ComponentHub"/> to the path <paramref name="path"/> and associates
/// the component <paramref name="componentType"/> to this hub instance as the given DOM <paramref name="selector"/>.
/// </summary>
/// <param name="routes">The <see cref="RouteBuilder"/>.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/>.</param>
/// <param name="componentType">The first <see cref="IComponent"/> associated with this <see cref="ComponentHub"/>.</param>
/// <param name="selector">The selector for the <paramref name="componentType"/>.</param>
/// <param name="path">The path to map to which the <see cref="ComponentHub"/> will be mapped.</param>
/// <returns>The <see cref="IEndpointConventionBuilder"/>.</returns>
public static IEndpointConventionBuilder MapComponentHub(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
Type componentType,
string selector,
string path)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (path == null)
@ -104,7 +104,7 @@ namespace Microsoft.AspNetCore.Builder
throw new ArgumentNullException(nameof(selector));
}
return routes.MapHub<ComponentHub>(path).AddComponent(componentType, selector);
return endpoints.MapHub<ComponentHub>(path).AddComponent(componentType, selector);
}
}
}

View File

@ -100,7 +100,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
{
throw new InvalidOperationException(
$"{nameof(ComponentHub)} doesn't have an associated endpoint. " +
"Use 'app.UseRouting(routes => routes.MapComponentHub<App>(\"app\"))' to register your hub.");
"Use 'app.UseEndpoints(endpoints => endpoints.MapComponentHub<App>(\"app\"))' to register your hub.");
}
var componentsMetadata = endpoint.Metadata.OfType<ComponentDescriptor>().ToList();

View File

@ -29,10 +29,13 @@ namespace ComponentsApp.Server
}
app.UseStaticFiles();
app.UseRouting(builder =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
builder.MapRazorPages();
builder.MapComponentHub<App.App>("app");
endpoints.MapRazorPages();
endpoints.MapComponentHub<App.App>("app");
});
}
}

View File

@ -38,24 +38,36 @@ namespace TestServer
}
AllowCorsForAnyLocalhostPort(app);
app.UseMvc();
app.UseRouting();
// Mount the server-side Blazor app on /subdir
app.Map("/subdir", subdirApp =>
{
// The following two lines are equivalent to:
// subdirApp.UseServerSideBlazor<BasicTestApp.Startup>();
// However it's expressed using UseSignalR+UseBlazor as a way of checking that
// we're not relying on any extra magic inside UseServerSideBlazor, since it's
// endpoints.MapComponentsHub<Index>();
//
// However it's expressed using routing as a way of checking that
// we're not relying on any extra magic inside MapComponentsHub, since it's
// important that people can set up these bits of middleware manually (e.g., to
// swap in UseAzureSignalR instead of UseSignalR).
subdirApp.UseRouting(routes =>
routes.MapHub<ComponentHub>(ComponentHub.DefaultPath).AddComponent<Index>(selector: "root"));
subdirApp.UseRouting();
subdirApp.UseEndpoints(endpoints =>
{
endpoints.MapHub<ComponentHub>(ComponentHub.DefaultPath).AddComponent<Index>(selector: "root");
});
subdirApp.MapWhen(
ctx => ctx.Features.Get<IEndpointFeature>()?.Endpoint == null,
blazorBuilder => blazorBuilder.UseBlazor<BasicTestApp.Startup>());
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
private static void AllowCorsForAnyLocalhostPort(IApplicationBuilder app)

View File

@ -82,11 +82,17 @@ namespace Microsoft.AspNetCore.Builder.Internal
{
RequestDelegate app = context =>
{
// Implicitly execute matched endpoint at the end of the pipeline instead of returning 404
var endpointRequestDelegate = context.GetEndpoint()?.RequestDelegate;
// If we reach the end of the pipeline, but we have an endpoint, then something unexpected has happened.
// This could happen if user code sets an endpoint, but they forgot to add the UseEndpoint middleware.
var endpoint = context.GetEndpoint();
var endpointRequestDelegate = endpoint?.RequestDelegate;
if (endpointRequestDelegate != null)
{
return endpointRequestDelegate(context);
var message =
$"The request reached the end of the pipeline without executing the endpoint: '{endpoint.DisplayName}'. " +
$"Please register the EndpointMiddleware using '{nameof(IApplicationBuilder)}.UseEndpoints(...)' if using " +
$"routing, or '{nameof(IApplicationBuilder)}.UseEndpointExecutor()' if not using routing.";
throw new InvalidOperationException(message);
}
context.Response.StatusCode = 404;

View File

@ -1,6 +1,7 @@
// 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.Http.Endpoints;
@ -23,7 +24,7 @@ namespace Microsoft.AspNetCore.Builder.Internal
}
[Fact]
public void BuildImplicitlyCallsMatchedEndpointAsLastStep()
public async Task BuildImplicitlyThrowsForMatchedEndpointAsLastStep()
{
var builder = new ApplicationBuilder(null);
var app = builder.Build();
@ -41,9 +42,14 @@ namespace Microsoft.AspNetCore.Builder.Internal
var httpContext = new DefaultHttpContext();
httpContext.SetEndpoint(endpoint);
app.Invoke(httpContext);
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => app.Invoke(httpContext));
Assert.True(endpointCalled);
var expected =
"The request reached the end of the pipeline without executing the endpoint: 'Test endpoint'. " +
"Please register the EndpointMiddleware using 'IApplicationBuilder.UseEndpoints(...)' if " +
"using routing, or 'IApplicationBuilder.UseEndpointExecutor()' if not using routing.";
Assert.Equal(expected, ex.Message);
Assert.False(endpointCalled);
}
[Fact]
@ -87,4 +93,4 @@ namespace Microsoft.AspNetCore.Builder.Internal
Assert.Equal("value1", builder1.Properties["test"]);
}
}
}
}

View File

@ -5,23 +5,24 @@ namespace Microsoft.AspNetCore.Builder
{
public static partial class EndpointRouteBuilderExtensions
{
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder Map(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, Microsoft.AspNetCore.Routing.Patterns.RoutePattern pattern, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder Map(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string pattern, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapDelete(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string pattern, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapGet(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string pattern, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapMethods(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string pattern, System.Collections.Generic.IEnumerable<string> httpMethods, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapPost(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string pattern, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapPut(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string pattern, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder Map(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, Microsoft.AspNetCore.Routing.Patterns.RoutePattern pattern, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder Map(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string pattern, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapDelete(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string pattern, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapGet(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string pattern, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapMethods(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string pattern, System.Collections.Generic.IEnumerable<string> httpMethods, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapPost(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string pattern, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapPut(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string pattern, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate) { throw null; }
}
public static partial class EndpointRoutingApplicationBuilderExtensions
{
public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseEndpoint(this Microsoft.AspNetCore.Builder.IApplicationBuilder builder) { throw null; }
public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseRouting(this Microsoft.AspNetCore.Builder.IApplicationBuilder builder, System.Action<Microsoft.AspNetCore.Routing.IEndpointRouteBuilder> configure) { throw null; }
public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseEndpointExecutor(this Microsoft.AspNetCore.Builder.IApplicationBuilder builder) { throw null; }
public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseEndpoints(this Microsoft.AspNetCore.Builder.IApplicationBuilder builder, System.Action<Microsoft.AspNetCore.Routing.IEndpointRouteBuilder> configure) { throw null; }
public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseRouting(this Microsoft.AspNetCore.Builder.IApplicationBuilder builder) { throw null; }
}
public static partial class FallbackEndpointRouteBuilderExtensions
{
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapFallback(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapFallback(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string pattern, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapFallback(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapFallback(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string pattern, Microsoft.AspNetCore.Http.RequestDelegate requestDelegate) { throw null; }
}
public static partial class MapRouteRouteBuilderExtensions
{

View File

@ -26,77 +26,77 @@ namespace Microsoft.AspNetCore.Builder
/// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP GET requests
/// for the specified pattern.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param>
/// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns>
public static IEndpointConventionBuilder MapGet(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string pattern,
RequestDelegate requestDelegate)
{
return MapMethods(routes, pattern, GetVerb, requestDelegate);
return MapMethods(endpoints, pattern, GetVerb, requestDelegate);
}
/// <summary>
/// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP POST requests
/// for the specified pattern.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param>
/// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns>
public static IEndpointConventionBuilder MapPost(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string pattern,
RequestDelegate requestDelegate)
{
return MapMethods(routes, pattern, PostVerb, requestDelegate);
return MapMethods(endpoints, pattern, PostVerb, requestDelegate);
}
/// <summary>
/// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP PUT requests
/// for the specified pattern.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param>
/// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns>
public static IEndpointConventionBuilder MapPut(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string pattern,
RequestDelegate requestDelegate)
{
return MapMethods(routes, pattern, PutVerb, requestDelegate);
return MapMethods(endpoints, pattern, PutVerb, requestDelegate);
}
/// <summary>
/// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP DELETE requests
/// for the specified pattern.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param>
/// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns>
public static IEndpointConventionBuilder MapDelete(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string pattern,
RequestDelegate requestDelegate)
{
return MapMethods(routes, pattern, DeleteVerb, requestDelegate);
return MapMethods(endpoints, pattern, DeleteVerb, requestDelegate);
}
/// <summary>
/// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP requests
/// for the specified HTTP methods and pattern.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param>
/// <param name="httpMethods">HTTP methods that the endpoint will match.</param>
/// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns>
public static IEndpointConventionBuilder MapMethods(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string pattern,
IEnumerable<string> httpMethods,
RequestDelegate requestDelegate)
@ -106,7 +106,7 @@ namespace Microsoft.AspNetCore.Builder
throw new ArgumentNullException(nameof(httpMethods));
}
var builder = routes.Map(RoutePatternFactory.Parse(pattern), requestDelegate);
var builder = endpoints.Map(RoutePatternFactory.Parse(pattern), requestDelegate);
builder.WithDisplayName($"{pattern} HTTP: {string.Join(", ", httpMethods)}");
builder.WithMetadata(new HttpMethodMetadata(httpMethods));
return builder;
@ -116,34 +116,34 @@ namespace Microsoft.AspNetCore.Builder
/// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP requests
/// for the specified pattern.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param>
/// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns>
public static IEndpointConventionBuilder Map(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string pattern,
RequestDelegate requestDelegate)
{
return Map(routes, RoutePatternFactory.Parse(pattern), requestDelegate);
return Map(endpoints, RoutePatternFactory.Parse(pattern), requestDelegate);
}
/// <summary>
/// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP requests
/// for the specified pattern.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param>
/// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns>
public static IEndpointConventionBuilder Map(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
RoutePattern pattern,
RequestDelegate requestDelegate)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (pattern == null)
@ -178,11 +178,11 @@ namespace Microsoft.AspNetCore.Builder
}
}
var dataSource = routes.DataSources.OfType<ModelEndpointDataSource>().FirstOrDefault();
var dataSource = endpoints.DataSources.OfType<ModelEndpointDataSource>().FirstOrDefault();
if (dataSource == null)
{
dataSource = new ModelEndpointDataSource();
routes.DataSources.Add(dataSource);
endpoints.DataSources.Add(dataSource);
}
return dataSource.AddEndpointBuilder(builder);

View File

@ -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 Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.Extensions.DependencyInjection;
@ -11,17 +12,66 @@ namespace Microsoft.AspNetCore.Builder
{
public static class EndpointRoutingApplicationBuilderExtensions
{
// Property key is used by MVC package to check that routing is registered
private const string EndpointRoutingRegisteredKey = "__EndpointRoutingMiddlewareRegistered";
private const string EndpointRouteBuilder = "__EndpointRouteBuilder";
/// <summary>
/// Adds a <see cref="EndpointRoutingMiddleware"/> middleware to the specified <see cref="IApplicationBuilder"/>
/// Adds a <see cref="EndpointRoutingMiddleware"/> middleware to the specified <see cref="IApplicationBuilder"/>.
/// </summary>
/// <param name="builder">The <see cref="IApplicationBuilder"/> to add the middleware to.</param>
/// <returns>A reference to this instance after the operation has completed.</returns>
/// <remarks>
/// <para>
/// A call to <see cref="UseRouting(IApplicationBuilder)"/> must be followed by a call to
/// <see cref="UseEndpoints(IApplicationBuilder, Action{IEndpointRouteBuilder})"/> for the same <see cref="IApplicationBuilder"/>
/// instance.
/// </para>
/// <para>
/// The <see cref="EndpointRoutingMiddleware"/> defines a point in the middleware pipeline where routing decisions are
/// made, and an <see cref="Endpoint"/> is associated with the <see cref="HttpContext"/>. The <see cref="EndpointMiddleware"/>
/// defines a point in the middleware pipeline where the current <see cref="Endpoint"/> is executed. Middleware between
/// the <see cref="EndpointRoutingMiddleware"/> and <see cref="EndpointMiddleware"/> may observe or change the
/// <see cref="Endpoint"/> associated with the <see cref="HttpContext"/>.
/// </para>
/// </remarks>
public static IApplicationBuilder UseRouting(this IApplicationBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
VerifyRoutingServicesAreRegistered(builder);
var endpointRouteBuilder = new DefaultEndpointRouteBuilder(builder);
builder.Properties[EndpointRouteBuilder] = endpointRouteBuilder;
return builder.UseMiddleware<EndpointRoutingMiddleware>(endpointRouteBuilder);
}
/// <summary>
/// Adds a <see cref="EndpointMiddleware"/> middleware to the specified <see cref="IApplicationBuilder"/>
/// with the <see cref="EndpointDataSource"/> instances built from configured <see cref="IEndpointRouteBuilder"/>.
/// The <see cref="EndpointMiddleware"/> will execute the <see cref="Endpoint"/> associated with the current
/// request.
/// </summary>
/// <param name="builder">The <see cref="IApplicationBuilder"/> to add the middleware to.</param>
/// <param name="configure">An <see cref="Action{IEndpointRouteBuilder}"/> to configure the provided <see cref="IEndpointRouteBuilder"/>.</param>
/// <returns>A reference to this instance after the operation has completed.</returns>
public static IApplicationBuilder UseRouting(this IApplicationBuilder builder, Action<IEndpointRouteBuilder> configure)
/// <remarks>
/// <para>
/// A call to <see cref="UseEndpoints(IApplicationBuilder, Action{IEndpointRouteBuilder})"/> must be preceded by a call to
/// <see cref="UseRouting(IApplicationBuilder)"/> for the same <see cref="IApplicationBuilder"/>
/// instance.
/// </para>
/// <para>
/// The <see cref="EndpointRoutingMiddleware"/> defines a point in the middleware pipeline where routing decisions are
/// made, and an <see cref="Endpoint"/> is associated with the <see cref="HttpContext"/>. The <see cref="EndpointMiddleware"/>
/// defines a point in the middleware pipeline where the current <see cref="Endpoint"/> is executed. Middleware between
/// the <see cref="EndpointRoutingMiddleware"/> and <see cref="EndpointMiddleware"/> may observe or change the
/// <see cref="Endpoint"/> associated with the <see cref="HttpContext"/>.
/// </para>
/// </remarks>
public static IApplicationBuilder UseEndpoints(this IApplicationBuilder builder, Action<IEndpointRouteBuilder> configure)
{
if (builder == null)
{
@ -33,57 +83,52 @@ namespace Microsoft.AspNetCore.Builder
throw new ArgumentNullException(nameof(configure));
}
VerifyRoutingIsRegistered(builder);
VerifyRoutingServicesAreRegistered(builder);
var routeOptions = builder.ApplicationServices.GetRequiredService<IOptions<RouteOptions>>();
EndpointDataSource middlewareEndpointDataSource;
VerifyEndpointRoutingMiddlewareIsRegistered(builder, out var endpointRouteBuilder);
var endpointRouteBuilder = builder.ApplicationServices.GetRequiredService<IEndpointRouteBuilder>();
if (endpointRouteBuilder is DefaultEndpointRouteBuilder defaultEndpointRouteBuilder)
{
defaultEndpointRouteBuilder.ApplicationBuilder = builder;
}
configure(endpointRouteBuilder);
// Yes, this mutates an IOptions. We're registering data sources in a global collection which
// can be used for discovery of endpoints or URL generation.
//
// Each middleware gets its own collection of data sources, and all of those data sources also
// get added to a global collection.
var routeOptions = builder.ApplicationServices.GetRequiredService<IOptions<RouteOptions>>();
foreach (var dataSource in endpointRouteBuilder.DataSources)
{
routeOptions.Value.EndpointDataSources.Add(dataSource);
}
// Create endpoint data source for data sources registered in configure
middlewareEndpointDataSource = new CompositeEndpointDataSource(endpointRouteBuilder.DataSources);
builder.Properties[EndpointRoutingRegisteredKey] = true;
return builder.UseMiddleware<EndpointRoutingMiddleware>(middlewareEndpointDataSource);
return builder.UseMiddleware<EndpointMiddleware>();
}
/// <summary>
/// Adds a <see cref="EndpointMiddleware"/> middleware to the specified <see cref="IApplicationBuilder"/>.
/// Adds a <see cref="EndpointMiddleware"/> middleware to the specified <see cref="IApplicationBuilder"/> that will
/// execute the <see cref="Endpoint"/> associated with the current request.
/// </summary>
/// <param name="builder">The <see cref="IApplicationBuilder"/> to add the middleware to.</param>
/// <returns>A reference to this instance after the operation has completed.</returns>
public static IApplicationBuilder UseEndpoint(this IApplicationBuilder builder)
/// <remarks>
/// <para>
/// A call to <see cref="UseEndpointExecutor(IApplicationBuilder)"/> may be used to execute an endpoint with
/// using <see cref="UseRouting(IApplicationBuilder)" />. This enables applications to use endpoints with other
/// middleware not related to routing.
/// </para>
/// </remarks>
public static IApplicationBuilder UseEndpointExecutor(this IApplicationBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
VerifyRoutingIsRegistered(builder);
if (!builder.Properties.TryGetValue(EndpointRoutingRegisteredKey, out _))
{
var message = $"{nameof(EndpointRoutingMiddleware)} must be added to the request execution pipeline before {nameof(EndpointMiddleware)}. " +
$"Please add {nameof(EndpointRoutingMiddleware)} by calling '{nameof(IApplicationBuilder)}.{nameof(UseRouting)}' inside the call to 'Configure(...)' in the application startup code.";
throw new InvalidOperationException(message);
}
// Note: we don't require routing services either.
return builder.UseMiddleware<EndpointMiddleware>();
}
private static void VerifyRoutingIsRegistered(IApplicationBuilder app)
private static void VerifyRoutingServicesAreRegistered(IApplicationBuilder app)
{
// Verify if AddRouting was done before calling UseEndpointRouting/UseEndpoint
// We use the RoutingMarkerService to make sure if all the services were added.
@ -95,5 +140,32 @@ namespace Microsoft.AspNetCore.Builder
"ConfigureServices(...)"));
}
}
private static void VerifyEndpointRoutingMiddlewareIsRegistered(IApplicationBuilder app, out DefaultEndpointRouteBuilder endpointRouteBuilder)
{
if (!app.Properties.TryGetValue(EndpointRouteBuilder, out var obj))
{
var message =
$"{nameof(EndpointRoutingMiddleware)} matches endpoints setup by {nameof(EndpointMiddleware)} and so must be added to the request " +
$"execution pipeline before {nameof(EndpointMiddleware)}. " +
$"Please add {nameof(EndpointRoutingMiddleware)} by calling '{nameof(IApplicationBuilder)}.{nameof(UseRouting)}' inside the call " +
$"to 'Configure(...)' in the application startup code.";
throw new InvalidOperationException(message);
}
// If someone messes with this, just let it crash.
endpointRouteBuilder = (DefaultEndpointRouteBuilder)obj;
// This check handles the case where Map or something else that forks the pipeline is called between the two
// routing middleware.
if (!object.ReferenceEquals(app, endpointRouteBuilder.ApplicationBuilder))
{
var message =
$"The {nameof(EndpointRoutingMiddleware)} and {nameof(EndpointMiddleware)} must be added to the same {nameof(IApplicationBuilder)} instance. " +
$"To use Endpoint Routing with 'Map(...)', make sure to call '{nameof(IApplicationBuilder)}.{nameof(UseRouting)}' before " +
$"'{nameof(IApplicationBuilder)}.{nameof(UseEndpoints)}' for each branch of the middleware pipeline.";
throw new InvalidOperationException(message);
}
}
}
}

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Builder
/// Adds a specialized <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that will match
/// requests for non-file-names with the lowest possible priority.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param>
/// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns>
/// <remarks>
@ -31,11 +31,11 @@ namespace Microsoft.AspNetCore.Builder
/// <c>{*path:nonfile}</c>. The order of the registered endpoint will be <c>int.MaxValue</c>.
/// </para>
/// </remarks>
public static IEndpointConventionBuilder MapFallback(this IEndpointRouteBuilder routes, RequestDelegate requestDelegate)
public static IEndpointConventionBuilder MapFallback(this IEndpointRouteBuilder endpoints, RequestDelegate requestDelegate)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (requestDelegate == null)
@ -43,14 +43,14 @@ namespace Microsoft.AspNetCore.Builder
throw new ArgumentNullException(nameof(requestDelegate));
}
return routes.MapFallback("{*path:nonfile}", requestDelegate);
return endpoints.MapFallback("{*path:nonfile}", requestDelegate);
}
/// <summary>
/// Adds a specialized <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that will match
/// the provided pattern with the lowest possible priority.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="requestDelegate">The delegate executed when the endpoint is matched.</param>
/// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns>
@ -68,13 +68,13 @@ namespace Microsoft.AspNetCore.Builder
/// </para>
/// </remarks>
public static IEndpointConventionBuilder MapFallback(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string pattern,
RequestDelegate requestDelegate)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (pattern == null)
@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Builder
throw new ArgumentNullException(nameof(requestDelegate));
}
var conventionBuilder = routes.Map(pattern, requestDelegate);
var conventionBuilder = endpoints.Map(pattern, requestDelegate);
conventionBuilder.WithDisplayName("Fallback " + pattern);
conventionBuilder.Add(b => ((RouteEndpointBuilder)b).Order = int.MaxValue);
return conventionBuilder;

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
@ -9,12 +9,13 @@ namespace Microsoft.AspNetCore.Routing
{
internal class DefaultEndpointRouteBuilder : IEndpointRouteBuilder
{
public DefaultEndpointRouteBuilder()
public DefaultEndpointRouteBuilder(IApplicationBuilder applicationBuilder)
{
ApplicationBuilder = applicationBuilder ?? throw new ArgumentNullException(nameof(applicationBuilder));
DataSources = new List<EndpointDataSource>();
}
public IApplicationBuilder ApplicationBuilder { get; set; }
public IApplicationBuilder ApplicationBuilder { get; }
public IApplicationBuilder CreateApplicationBuilder() => ApplicationBuilder.New();
@ -22,4 +23,4 @@ namespace Microsoft.AspNetCore.Routing
public IServiceProvider ServiceProvider => ApplicationBuilder.ApplicationServices;
}
}
}

View File

@ -64,11 +64,6 @@ namespace Microsoft.Extensions.DependencyInjection
return new CompositeEndpointDataSource(dataSources);
});
//
// Endpoint Infrastructure
//
services.TryAddTransient<IEndpointRouteBuilder, DefaultEndpointRouteBuilder>();
//
// Default matcher implementation
//

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
@ -8,7 +8,6 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Routing
{
@ -23,8 +22,8 @@ namespace Microsoft.AspNetCore.Routing
public EndpointRoutingMiddleware(
MatcherFactory matcherFactory,
EndpointDataSource endpointDataSource,
ILogger<EndpointRoutingMiddleware> logger,
IEndpointRouteBuilder endpointRouteBuilder,
RequestDelegate next)
{
if (matcherFactory == null)
@ -32,25 +31,26 @@ namespace Microsoft.AspNetCore.Routing
throw new ArgumentNullException(nameof(matcherFactory));
}
if (endpointDataSource == null)
{
throw new ArgumentNullException(nameof(endpointDataSource));
}
if (logger == null)
{
throw new ArgumentNullException(nameof(logger));
}
if (endpointRouteBuilder == null)
{
throw new ArgumentNullException(nameof(endpointRouteBuilder));
}
if (next == null)
{
throw new ArgumentNullException(nameof(next));
}
_matcherFactory = matcherFactory;
_endpointDataSource = endpointDataSource;
_logger = logger;
_next = next;
_endpointDataSource = new CompositeEndpointDataSource(endpointRouteBuilder.DataSources);
}
public async Task Invoke(HttpContext httpContext)
@ -165,4 +165,4 @@ namespace Microsoft.AspNetCore.Routing
}
}
}
}
}

View File

@ -6,13 +6,13 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder.Internal;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Endpoints;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.AspNetCore.Routing.TestObjects;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Moq;
using Xunit;
@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Builder
var app = new ApplicationBuilder(Mock.Of<IServiceProvider>());
// Act
var ex = Assert.Throws<InvalidOperationException>(() => app.UseRouting(builder => { }));
var ex = Assert.Throws<InvalidOperationException>(() => app.UseRouting());
// Assert
Assert.Equal(
@ -44,7 +44,7 @@ namespace Microsoft.AspNetCore.Builder
var app = new ApplicationBuilder(Mock.Of<IServiceProvider>());
// Act
var ex = Assert.Throws<InvalidOperationException>(() => app.UseEndpoint());
var ex = Assert.Throws<InvalidOperationException>(() => app.UseEndpoints(endpoints => { }));
// Assert
Assert.Equal(
@ -62,7 +62,7 @@ namespace Microsoft.AspNetCore.Builder
var app = new ApplicationBuilder(services);
app.UseRouting(builder => { });
app.UseRouting();
var appFunc = app.Build();
var httpContext = new DefaultHttpContext();
@ -89,9 +89,11 @@ namespace Microsoft.AspNetCore.Builder
var app = new ApplicationBuilder(services);
app.UseRouting(builder =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
builder.DataSources.Add(new DefaultEndpointDataSource(endpoint));
endpoints.DataSources.Add(new DefaultEndpointDataSource(endpoint));
});
var appFunc = app.Build();
@ -107,7 +109,7 @@ namespace Microsoft.AspNetCore.Builder
}
[Fact]
public void UseEndpoint_WithoutRoutingServicesRegistered_Throws()
public void UseEndpoint_WithoutEndpointRoutingMiddleware_Throws()
{
// Arrange
var services = CreateServices();
@ -115,16 +117,38 @@ namespace Microsoft.AspNetCore.Builder
var app = new ApplicationBuilder(services);
// Act
var ex = Assert.Throws<InvalidOperationException>(() => app.UseEndpoint());
var ex = Assert.Throws<InvalidOperationException>(() => app.UseEndpoints(endpoints => { }));
// Assert
Assert.Equal(
"EndpointRoutingMiddleware must be added to the request execution pipeline before EndpointMiddleware. " +
"EndpointRoutingMiddleware matches endpoints setup by EndpointMiddleware and so must be added to the request " +
"execution pipeline before EndpointMiddleware. " +
"Please add EndpointRoutingMiddleware by calling 'IApplicationBuilder.UseRouting' " +
"inside the call to 'Configure(...)' in the application startup code.",
ex.Message);
}
[Fact]
public void UseEndpoint_WithApplicationBuilderMismatch_Throws()
{
// Arrange
var services = CreateServices();
var app = new ApplicationBuilder(services);
app.UseRouting();
// Act
var ex = Assert.Throws<InvalidOperationException>(() => app.Map("/Test", b => b.UseEndpoints(endpoints => { })));
// Assert
Assert.Equal(
"The EndpointRoutingMiddleware and EndpointMiddleware must be added to the same IApplicationBuilder instance. " +
"To use Endpoint Routing with 'Map(...)', make sure to call 'IApplicationBuilder.UseRouting' before " +
"'IApplicationBuilder.UseEndpoints' for each branch of the middleware pipeline.",
ex.Message);
}
[Fact]
public async Task UseEndpoint_ServicesRegisteredAndEndpointRoutingRegistered_NoMatch_DoesNotSetFeature()
{
@ -133,8 +157,8 @@ namespace Microsoft.AspNetCore.Builder
var app = new ApplicationBuilder(services);
app.UseRouting(builder => { });
app.UseEndpoint();
app.UseRouting();
app.UseEndpoints(endpoints => { });
var appFunc = app.Build();
var httpContext = new DefaultHttpContext();
@ -147,7 +171,7 @@ namespace Microsoft.AspNetCore.Builder
}
[Fact]
public void UseRouting_CallWithBuilder_SetsEndpointDataSource()
public void UseEndpoints_CallWithBuilder_SetsEndpointDataSource()
{
// Arrange
var matcherEndpointDataSources = new List<EndpointDataSource>();
@ -165,13 +189,15 @@ namespace Microsoft.AspNetCore.Builder
var app = new ApplicationBuilder(services);
// Act
app.UseRouting(builder =>
app.UseRouting();
app.UseEndpoints(builder =>
{
builder.Map("/1", d => null).WithDisplayName("Test endpoint 1");
builder.Map("/2", d => null).WithDisplayName("Test endpoint 2");
});
app.UseRouting(builder =>
app.UseRouting();
app.UseEndpoints(builder =>
{
builder.Map("/3", d => null).WithDisplayName("Test endpoint 3");
builder.Map("/4", d => null).WithDisplayName("Test endpoint 4");
@ -185,16 +211,83 @@ namespace Microsoft.AspNetCore.Builder
// Assert
Assert.Equal(2, matcherEndpointDataSources.Count);
// Each middleware has its own endpoints
// each UseRouter has its own data source collection
Assert.Collection(matcherEndpointDataSources[0].Endpoints,
e => Assert.Equal("Test endpoint 1", e.DisplayName),
e => Assert.Equal("Test endpoint 2", e.DisplayName));
Assert.Collection(matcherEndpointDataSources[1].Endpoints,
e => Assert.Equal("Test endpoint 3", e.DisplayName),
e => Assert.Equal("Test endpoint 4", e.DisplayName));
var compositeEndpointBuilder = services.GetRequiredService<EndpointDataSource>();
// Global collection has all endpoints
Assert.Collection(compositeEndpointBuilder.Endpoints,
e => Assert.Equal("Test endpoint 1", e.DisplayName),
e => Assert.Equal("Test endpoint 2", e.DisplayName),
e => Assert.Equal("Test endpoint 3", e.DisplayName),
e => Assert.Equal("Test endpoint 4", e.DisplayName));
}
// Verifies that it's possible to use endpoints and map together.
[Fact]
public void UseEndpoints_CallWithBuilder_SetsEndpointDataSource_WithMap()
{
// Arrange
var matcherEndpointDataSources = new List<EndpointDataSource>();
var matcherFactoryMock = new Mock<MatcherFactory>();
matcherFactoryMock
.Setup(m => m.CreateMatcher(It.IsAny<EndpointDataSource>()))
.Callback((EndpointDataSource arg) =>
{
matcherEndpointDataSources.Add(arg);
})
.Returns(new TestMatcher(false));
var services = CreateServices(matcherFactoryMock.Object);
var app = new ApplicationBuilder(services);
// Act
app.UseRouting();
app.Map("/foo", b =>
{
b.UseRouting();
b.UseEndpoints(builder =>
{
builder.Map("/1", d => null).WithDisplayName("Test endpoint 1");
builder.Map("/2", d => null).WithDisplayName("Test endpoint 2");
});
});
app.UseEndpoints(builder =>
{
builder.Map("/3", d => null).WithDisplayName("Test endpoint 3");
builder.Map("/4", d => null).WithDisplayName("Test endpoint 4");
});
// This triggers the middleware to be created and the matcher factory to be called
// with the datasource we want to test
var requestDelegate = app.Build();
requestDelegate(new DefaultHttpContext());
requestDelegate(new DefaultHttpContext() { Request = { Path = "/Foo", }, });
// Assert
Assert.Equal(2, matcherEndpointDataSources.Count);
// Each UseRouter has its own data source
Assert.Collection(matcherEndpointDataSources[1].Endpoints, // app.UseRouter
e => Assert.Equal("Test endpoint 1", e.DisplayName),
e => Assert.Equal("Test endpoint 2", e.DisplayName));
Assert.Collection(matcherEndpointDataSources[0].Endpoints, // b.UseRouter
e => Assert.Equal("Test endpoint 3", e.DisplayName),
e => Assert.Equal("Test endpoint 4", e.DisplayName));
var compositeEndpointBuilder = services.GetRequiredService<EndpointDataSource>();
// Global middleware has all endpoints
Assert.Collection(compositeEndpointBuilder.Endpoints,
e => Assert.Equal("Test endpoint 1", e.DisplayName),
@ -203,6 +296,37 @@ namespace Microsoft.AspNetCore.Builder
e => Assert.Equal("Test endpoint 4", e.DisplayName));
}
[Fact]
public async Task UseEndpointExecutor_RunsEndpoint()
{
// Arrange
var services = CreateServices();
var endpointRan = false;
var app = new ApplicationBuilder(services);
app.Use(next => context =>
{
context.SetEndpoint(new Endpoint(c =>
{
endpointRan = true;
return Task.CompletedTask;
}, new EndpointMetadataCollection(), "Test"));
return next(context);
});
app.UseEndpointExecutor(); // No services required, no UseRouting required
var appFunc = app.Build();
var httpContext = new DefaultHttpContext();
// Act
await appFunc(httpContext);
// Assert
Assert.True(endpointRan);
}
private IServiceProvider CreateServices()
{
return CreateServices(matcherFactory: null);

View File

@ -9,6 +9,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Patterns;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Builder
@ -29,7 +30,7 @@ namespace Microsoft.AspNetCore.Builder
public void MapEndpoint_StringPattern_BuildsEndpoint()
{
// Arrange
var builder = new DefaultEndpointRouteBuilder();
var builder = new DefaultEndpointRouteBuilder(Mock.Of<IApplicationBuilder>());
RequestDelegate requestDelegate = (d) => null;
// Act
@ -47,7 +48,7 @@ namespace Microsoft.AspNetCore.Builder
public void MapEndpoint_TypedPattern_BuildsEndpoint()
{
// Arrange
var builder = new DefaultEndpointRouteBuilder();
var builder = new DefaultEndpointRouteBuilder(Mock.Of<IApplicationBuilder>());
RequestDelegate requestDelegate = (d) => null;
// Act
@ -65,7 +66,7 @@ namespace Microsoft.AspNetCore.Builder
public void MapEndpoint_AttributesCollectedAsMetadata()
{
// Arrange
var builder = new DefaultEndpointRouteBuilder();
var builder = new DefaultEndpointRouteBuilder(Mock.Of<IApplicationBuilder>());
// Act
var endpointBuilder = builder.Map(RoutePatternFactory.Parse("/"), Handle);
@ -82,7 +83,7 @@ namespace Microsoft.AspNetCore.Builder
public void MapEndpoint_GeneratedDelegateWorks()
{
// Arrange
var builder = new DefaultEndpointRouteBuilder();
var builder = new DefaultEndpointRouteBuilder(Mock.Of<IApplicationBuilder>());
Expression<RequestDelegate> handler = context => Task.CompletedTask;
@ -98,7 +99,7 @@ namespace Microsoft.AspNetCore.Builder
public void MapEndpoint_PrecedenceOfMetadata_BuilderMetadataReturned()
{
// Arrange
var builder = new DefaultEndpointRouteBuilder();
var builder = new DefaultEndpointRouteBuilder(Mock.Of<IApplicationBuilder>());
// Act
var endpointBuilder = builder.MapMethods("/", new[] { "METHOD" }, HandleHttpMetdata);

View File

@ -4,6 +4,7 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Matching;
@ -153,8 +154,8 @@ namespace Microsoft.AspNetCore.Routing
var middleware = new EndpointRoutingMiddleware(
matcherFactory,
new DefaultEndpointDataSource(),
logger,
new DefaultEndpointRouteBuilder(Mock.Of<IApplicationBuilder>()),
next);
return middleware;

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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.Text;
@ -21,7 +21,9 @@ namespace Benchmarks
public void Configure(IApplicationBuilder app)
{
app.UseRouting(builder =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
var endpointDataSource = new DefaultEndpointDataSource(new[]
{
@ -41,10 +43,8 @@ namespace Benchmarks
displayName: "Plaintext"),
});
builder.DataSources.Add(endpointDataSource);
endpoints.DataSources.Add(endpointDataSource);
});
app.UseEndpoint();
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
@ -15,23 +15,23 @@ namespace RoutingSandbox.Framework
{
public static class FrameworkEndpointRouteBuilderExtensions
{
public static IEndpointConventionBuilder MapFramework(this IEndpointRouteBuilder routes, Action<FrameworkConfigurationBuilder> configure)
public static IEndpointConventionBuilder MapFramework(this IEndpointRouteBuilder endpoints, Action<FrameworkConfigurationBuilder> configure)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
var dataSource = routes.ServiceProvider.GetRequiredService<FrameworkEndpointDataSource>();
var dataSource = endpoints.ServiceProvider.GetRequiredService<FrameworkEndpointDataSource>();
var configurationBuilder = new FrameworkConfigurationBuilder(dataSource);
configure(configurationBuilder);
routes.DataSources.Add(dataSource);
endpoints.DataSources.Add(dataSource);
return dataSource;
}

View File

@ -13,18 +13,18 @@ namespace Microsoft.AspNetCore.Builder
{
public static class EndpointRouteBuilderExtensions
{
public static IEndpointConventionBuilder MapHello(this IEndpointRouteBuilder routes, string pattern, string greeter)
public static IEndpointConventionBuilder MapHello(this IEndpointRouteBuilder endpoints, string pattern, string greeter)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
var pipeline = routes.CreateApplicationBuilder()
var pipeline = endpoints.CreateApplicationBuilder()
.UseHello(greeter)
.Build();
return routes.Map(pattern, pipeline);
return endpoints.Map(pattern, pipeline);
}
}
}

View File

@ -32,11 +32,13 @@ namespace RoutingSandbox
public void Configure(IApplicationBuilder app)
{
app.UseRouting(builder =>
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
builder.MapHello("/helloworld", "World");
endpoints.MapHello("/helloworld", "World");
builder.MapGet(
endpoints.MapGet(
"/",
(httpContext) =>
{
@ -58,7 +60,7 @@ namespace RoutingSandbox
response.ContentType = "text/plain";
return response.WriteAsync(sb.ToString());
});
builder.MapGet(
endpoints.MapGet(
"/plaintext",
(httpContext) =>
{
@ -69,7 +71,7 @@ namespace RoutingSandbox
response.ContentLength = payloadLength;
return response.Body.WriteAsync(_plainTextPayload, 0, payloadLength);
});
builder.MapGet(
endpoints.MapGet(
"/graph",
(httpContext) =>
{
@ -83,11 +85,11 @@ namespace RoutingSandbox
return Task.CompletedTask;
}).WithDisplayName("DFA Graph");
builder.MapGet("/attributes", HandlerWithAttributes);
endpoints.MapGet("/attributes", HandlerWithAttributes);
builder.Map("/getwithattributes", Handler);
endpoints.Map("/getwithattributes", Handler);
builder.MapFramework(frameworkBuilder =>
endpoints.MapFramework(frameworkBuilder =>
{
frameworkBuilder.AddPattern("/transform/{hub:slugify=TestHub}/{method:slugify=TestMethod}");
frameworkBuilder.AddPattern("/{hub}/{method=TestMethod}");
@ -98,7 +100,6 @@ namespace RoutingSandbox
});
});
app.UseStaticFiles();
}
[Authorize]

View File

@ -13,18 +13,18 @@ namespace Microsoft.AspNetCore.Builder
{
public static class EndpointRouteBuilderExtensions
{
public static IEndpointConventionBuilder MapHello(this IEndpointRouteBuilder routes, string template, string greeter)
public static IEndpointConventionBuilder MapHello(this IEndpointRouteBuilder endpoints, string template, string greeter)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
var pipeline = routes.CreateApplicationBuilder()
var pipeline = endpoints.CreateApplicationBuilder()
.UseHello(greeter)
.Build();
return routes.Map(template, pipeline).WithDisplayName("Hello " + greeter);
return endpoints.Map(template, pipeline).WithDisplayName("Hello " + greeter);
}
}
}

View File

@ -16,22 +16,21 @@ namespace RoutingWebSite
public void Configure(IApplicationBuilder app)
{
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapFallback("/prefix/{*path:nonfile}", (context) =>
endpoints.MapFallback("/prefix/{*path:nonfile}", (context) =>
{
return context.Response.WriteAsync("FallbackCustomPattern");
});
routes.MapFallback((context) =>
endpoints.MapFallback((context) =>
{
return context.Response.WriteAsync("FallbackDefaultPattern");
});
routes.MapHello("/helloworld", "World");
endpoints.MapHello("/helloworld", "World");
});
app.UseEndpoint();
}
}
}

View File

@ -36,11 +36,20 @@ namespace RoutingWebSite
public void Configure(IApplicationBuilder app)
{
app.UseRouting(routes =>
{
routes.MapHello("/helloworld", "World");
app.UseStaticFiles();
routes.MapGet(
app.UseRouting();
app.Map("/Branch1", branch => SetupBranch(branch, "Branch1"));
app.Map("/Branch2", branch => SetupBranch(branch, "Branch2"));
// Imagine some more stuff here...
app.UseEndpoints(endpoints =>
{
endpoints.MapHello("/helloworld", "World");
endpoints.MapGet(
"/",
(httpContext) =>
{
@ -58,7 +67,7 @@ namespace RoutingWebSite
response.ContentType = "text/plain";
return response.WriteAsync(sb.ToString());
});
routes.MapGet(
endpoints.MapGet(
"/plaintext",
(httpContext) =>
{
@ -69,7 +78,7 @@ namespace RoutingWebSite
response.ContentLength = payloadLength;
return response.Body.WriteAsync(_plainTextPayload, 0, payloadLength);
});
routes.MapGet(
endpoints.MapGet(
"/convention",
(httpContext) =>
{
@ -79,7 +88,7 @@ namespace RoutingWebSite
{
b.Metadata.Add(new CustomMetadata());
});
routes.MapGet(
endpoints.MapGet(
"/withconstraints/{id:endsWith(_001)}",
(httpContext) =>
{
@ -88,7 +97,7 @@ namespace RoutingWebSite
response.ContentType = "text/plain";
return response.WriteAsync("WithConstraints");
});
routes.MapGet(
endpoints.MapGet(
"/withoptionalconstraints/{id:endsWith(_001)?}",
(httpContext) =>
{
@ -97,7 +106,7 @@ namespace RoutingWebSite
response.ContentType = "text/plain";
return response.WriteAsync("withoptionalconstraints");
});
routes.MapGet(
endpoints.MapGet(
"/WithSingleAsteriskCatchAll/{*path}",
(httpContext) =>
{
@ -109,7 +118,7 @@ namespace RoutingWebSite
return response.WriteAsync(
"Link: " + linkGenerator.GetPathByRouteValues(httpContext, "WithSingleAsteriskCatchAll", new { }));
}).WithMetadata(new RouteNameMetadata(routeName: "WithSingleAsteriskCatchAll"));
routes.MapGet(
endpoints.MapGet(
"/WithDoubleAsteriskCatchAll/{**path}",
(httpContext) =>
{
@ -122,34 +131,25 @@ namespace RoutingWebSite
"Link: " + linkGenerator.GetPathByRouteValues(httpContext, "WithDoubleAsteriskCatchAll", new { }));
}).WithMetadata(new RouteNameMetadata(routeName: "WithDoubleAsteriskCatchAll"));
MapHostEndpoint(routes);
MapHostEndpoint(routes, "*.0.0.1");
MapHostEndpoint(routes, "127.0.0.1");
MapHostEndpoint(routes, "*.0.0.1:5000", "*.0.0.1:5001");
MapHostEndpoint(routes, "contoso.com:*", "*.contoso.com:*");
MapHostEndpoint(endpoints);
MapHostEndpoint(endpoints, "*.0.0.1");
MapHostEndpoint(endpoints, "127.0.0.1");
MapHostEndpoint(endpoints, "*.0.0.1:5000", "*.0.0.1:5001");
MapHostEndpoint(endpoints, "contoso.com:*", "*.contoso.com:*");
});
app.Map("/Branch1", branch => SetupBranch(branch, "Branch1"));
app.Map("/Branch2", branch => SetupBranch(branch, "Branch2"));
app.UseStaticFiles();
// Imagine some more stuff here...
app.UseEndpoint();
}
private class CustomMetadata
{
}
private IEndpointConventionBuilder MapHostEndpoint(IEndpointRouteBuilder routes, params string[] hosts)
private IEndpointConventionBuilder MapHostEndpoint(IEndpointRouteBuilder endpoints, params string[] hosts)
{
var hostsDisplay = (hosts == null || hosts.Length == 0)
? "*:*"
: string.Join(",", hosts.Select(h => h.Contains(':') ? h : h + ":*"));
var conventionBuilder = routes.MapGet(
var conventionBuilder = endpoints.MapGet(
"api/DomainWildcard",
httpContext =>
{
@ -170,12 +170,11 @@ namespace RoutingWebSite
private void SetupBranch(IApplicationBuilder app, string name)
{
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapGet("api/get/{id}", (context) => context.Response.WriteAsync($"{name} - API Get {context.GetRouteData().Values["id"]}"));
endpoints.MapGet("api/get/{id}", (context) => context.Response.WriteAsync($"{name} - API Get {context.GetRouteData().Values["id"]}"));
});
app.UseEndpoint();
}
}
}

View File

@ -59,7 +59,15 @@ namespace ApiAuthSample
app.UseStaticFiles();
app.UseIdentityServer();
app.UseMvc();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

View File

@ -58,13 +58,15 @@ namespace IdentitySample.DefaultUI
app.UseStaticFiles();
app.UseAuthentication();
app.UseRouting();
app.UseMvc(routes =>
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}
}

View File

@ -70,13 +70,15 @@ namespace IdentitySample
app.UseStaticFiles();
app.UseAuthentication();
app.UseRouting();
app.UseMvc(routes =>
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}
}

View File

@ -57,9 +57,15 @@ namespace Identity.DefaultUI.WebSite
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseRouting();
app.UseMvc();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
}
}

View File

@ -77,9 +77,23 @@ namespace Identity.DefaultUI.WebSite
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseMvc();
// This has to be disabled due to https://github.com/aspnet/AspNetCore/issues/8387
//
// UseAuthorization does not currently work with Razor pages, and it impacts
// many of the tests here. Uncomment when this is fixed so that we test what is recommended
// for users.
//
//app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapRazorPages();
});
}
}
}

View File

@ -57,18 +57,18 @@ namespace SampleDestination
public void Configure(IApplicationBuilder app)
{
app.UseRouting(routing =>
{
routing.Map("/allow-origin", HandleRequest).WithCorsPolicy("AllowOrigin");
routing.Map("/allow-header-method", HandleRequest).WithCorsPolicy("AllowHeaderMethod");
routing.Map("/allow-credentials", HandleRequest).WithCorsPolicy("AllowCredentials");
routing.Map("/exposed-header", HandleRequest).WithCorsPolicy("ExposedHeader");
routing.Map("/allow-all", HandleRequest).WithCorsPolicy("AllowAll");
});
app.UseRouting();
app.UseCors();
app.UseEndpoint();
app.UseEndpoints(endpoints =>
{
endpoints.Map("/allow-origin", HandleRequest).WithCorsPolicy("AllowOrigin");
endpoints.Map("/allow-header-method", HandleRequest).WithCorsPolicy("AllowHeaderMethod");
endpoints.Map("/allow-credentials", HandleRequest).WithCorsPolicy("AllowCredentials");
endpoints.Map("/exposed-header", HandleRequest).WithCorsPolicy("ExposedHeader");
endpoints.Map("/allow-all", HandleRequest).WithCorsPolicy("AllowAll");
});
app.Run(async (context) =>
{

View File

@ -14,8 +14,8 @@ namespace Microsoft.AspNetCore.Builder
}
public static partial class HealthCheckEndpointRouteBuilderExtensions
{
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapHealthChecks(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string pattern) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapHealthChecks(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string pattern, Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions options) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapHealthChecks(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string pattern) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapHealthChecks(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string pattern, Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions options) { throw null; }
}
}
namespace Microsoft.AspNetCore.Diagnostics.HealthChecks

View File

@ -20,36 +20,36 @@ namespace Microsoft.AspNetCore.Builder
/// <summary>
/// Adds a health checks endpoint to the <see cref="IEndpointRouteBuilder"/> with the specified template.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the health checks endpoint to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the health checks endpoint to.</param>
/// <param name="pattern">The URL pattern of the health checks endpoint.</param>
/// <returns>A convention routes for the health checks endpoint.</returns>
public static IEndpointConventionBuilder MapHealthChecks(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string pattern)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
return MapHealthChecksCore(routes, pattern, null);
return MapHealthChecksCore(endpoints, pattern, null);
}
/// <summary>
/// Adds a health checks endpoint to the <see cref="IEndpointRouteBuilder"/> with the specified template and options.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the health checks endpoint to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the health checks endpoint to.</param>
/// <param name="pattern">The URL pattern of the health checks endpoint.</param>
/// <param name="options">A <see cref="HealthCheckOptions"/> used to configure the health checks.</param>
/// <returns>A convention routes for the health checks endpoint.</returns>
public static IEndpointConventionBuilder MapHealthChecks(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string pattern,
HealthCheckOptions options)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (options == null)
@ -57,12 +57,12 @@ namespace Microsoft.AspNetCore.Builder
throw new ArgumentNullException(nameof(options));
}
return MapHealthChecksCore(routes, pattern, options);
return MapHealthChecksCore(endpoints, pattern, options);
}
private static IEndpointConventionBuilder MapHealthChecksCore(IEndpointRouteBuilder routes, string pattern, HealthCheckOptions options)
private static IEndpointConventionBuilder MapHealthChecksCore(IEndpointRouteBuilder endpoints, string pattern, HealthCheckOptions options)
{
if (routes.ServiceProvider.GetService(typeof(HealthCheckService)) == null)
if (endpoints.ServiceProvider.GetService(typeof(HealthCheckService)) == null)
{
throw new InvalidOperationException(Resources.FormatUnableToFindServices(
nameof(IServiceCollection),
@ -72,11 +72,11 @@ namespace Microsoft.AspNetCore.Builder
var args = options != null ? new[] { Options.Create(options) } : Array.Empty<object>();
var pipeline = routes.CreateApplicationBuilder()
var pipeline = endpoints.CreateApplicationBuilder()
.UseMiddleware<HealthCheckMiddleware>(args)
.Build();
return routes.Map(pattern, pipeline).WithDisplayName(DefaultDisplayName);
return endpoints.Map(pattern, pipeline).WithDisplayName(DefaultDisplayName);
}
}
}

View File

@ -24,9 +24,10 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapHealthChecks("/healthz");
endpoints.MapHealthChecks("/healthz");
});
})
.ConfigureServices(services =>
@ -50,9 +51,10 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapHealthChecks("/healthz");
endpoints.MapHealthChecks("/healthz");
});
})
.ConfigureServices(services =>
@ -79,9 +81,10 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapHealthChecks("/healthz", new HealthCheckOptions
endpoints.MapHealthChecks("/healthz", new HealthCheckOptions
{
ResponseWriter = async (context, report) =>
{

View File

@ -42,11 +42,11 @@ namespace NodeServicesExamples
});
app.UseStaticFiles();
app.UseMvc(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapDefaultControllerRoute();
});
}

View File

@ -649,6 +649,9 @@ For the list of available options, please see [Webpack Hot Middleware docs](http
## Routing helper: MapSpaFallbackRoute
*Note: this functionality has been superseded by `endpoints.MapFallbackToFile(...)` provided by endpoint routing.
`MapFallbackToFile` behaves similarly to `MapSpaFallbackRoute`.*
In most single-page applications, you'll want client-side routing as well as your server-side routing. Most of the time, the two routing systems work independently without interfering. However, there is one case where things get challenging: identifying 404s.
If a request arrives for `/some/page`, and it doesn't match any server-side route, it's likely that you want to return HTML that starts up your client-side application, which probably understands the route `/some/page`. But if a request arrives for `/images/user-512.png`, and it doesn't match any server-side route or static file, it's **not** likely that your client-side application would handle it - you probably want to return a 404.

View File

@ -32,11 +32,10 @@ namespace Webpack
});
app.UseStaticFiles();
app.UseMvc(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapDefaultControllerRoute();
});
}

View File

@ -61,10 +61,10 @@ namespace Microsoft.AspNetCore.Builder
}
public static partial class StaticFilesEndpointRouteBuilderExtensions
{
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapFallbackToFile(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string filePath) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapFallbackToFile(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string filePath, Microsoft.AspNetCore.Builder.StaticFileOptions options) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapFallbackToFile(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string pattern, string filePath) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapFallbackToFile(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string pattern, string filePath, Microsoft.AspNetCore.Builder.StaticFileOptions options) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapFallbackToFile(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string filePath) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapFallbackToFile(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string filePath, Microsoft.AspNetCore.Builder.StaticFileOptions options) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapFallbackToFile(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string pattern, string filePath) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapFallbackToFile(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string pattern, string filePath, Microsoft.AspNetCore.Builder.StaticFileOptions options) { throw null; }
}
}
namespace Microsoft.AspNetCore.StaticFiles

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Builder
/// requests for non-filenames with the lowest possible priority. The request will be routed to a
/// <see cref="StaticFileMiddleware"/> that attempts to serve the file specified by <paramref name="filePath"/>.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/>.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/>.</param>
/// <param name="filePath">The file path of the file to serve.</param>
/// <returns>The <see cref="IEndpointRouteBuilder"/></returns>
/// <remarks>
@ -38,12 +38,12 @@ namespace Microsoft.AspNetCore.Builder
/// </para>
/// </remarks>
public static IEndpointConventionBuilder MapFallbackToFile(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string filePath)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (filePath == null)
@ -51,7 +51,7 @@ namespace Microsoft.AspNetCore.Builder
throw new ArgumentNullException(nameof(filePath));
}
return routes.MapFallback(CreateRequestDelegate(routes, filePath));
return endpoints.MapFallback(CreateRequestDelegate(endpoints, filePath));
}
/// <summary>
@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Builder
/// requests for non-filenames with the lowest possible priority. The request will be routed to a
/// <see cref="StaticFileMiddleware"/> that attempts to serve the file specified by <paramref name="filePath"/>.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/>.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/>.</param>
/// <param name="filePath">The file path of the file to serve.</param>
/// <param name="options"><see cref="StaticFileOptions"/> for the <see cref="StaticFileMiddleware"/>.</param>
/// <returns>The <see cref="IEndpointRouteBuilder"/></returns>
@ -76,13 +76,13 @@ namespace Microsoft.AspNetCore.Builder
/// </para>
/// </remarks>
public static IEndpointConventionBuilder MapFallbackToFile(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string filePath,
StaticFileOptions options)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (filePath == null)
@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Builder
throw new ArgumentNullException(nameof(filePath));
}
return routes.MapFallback(CreateRequestDelegate(routes, filePath, options));
return endpoints.MapFallback(CreateRequestDelegate(endpoints, filePath, options));
}
/// <summary>
@ -98,7 +98,7 @@ namespace Microsoft.AspNetCore.Builder
/// requests for non-filenames with the lowest possible priority. The request will be routed to a
/// <see cref="StaticFileMiddleware"/> that attempts to serve the file specified by <paramref name="filePath"/>.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/>.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/>.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="filePath">The file path of the file to serve.</param>
/// <returns>The <see cref="IEndpointRouteBuilder"/></returns>
@ -121,13 +121,13 @@ namespace Microsoft.AspNetCore.Builder
/// </para>
/// </remarks>
public static IEndpointConventionBuilder MapFallbackToFile(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string pattern,
string filePath)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (pattern == null)
@ -140,7 +140,7 @@ namespace Microsoft.AspNetCore.Builder
throw new ArgumentNullException(nameof(filePath));
}
return routes.MapFallback(pattern, CreateRequestDelegate(routes, filePath));
return endpoints.MapFallback(pattern, CreateRequestDelegate(endpoints, filePath));
}
/// <summary>
@ -148,7 +148,7 @@ namespace Microsoft.AspNetCore.Builder
/// requests for non-filenames with the lowest possible priority. The request will be routed to a
/// <see cref="StaticFileMiddleware"/> that attempts to serve the file specified by <paramref name="filePath"/>.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/>.</param>\
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/>.</param>\
/// <param name="pattern">The route pattern.</param>
/// <param name="filePath">The file path of the file to serve.</param>
/// <param name="options"><see cref="StaticFileOptions"/> for the <see cref="StaticFileMiddleware"/>.</param>
@ -169,14 +169,14 @@ namespace Microsoft.AspNetCore.Builder
/// </para>
/// </remarks>
public static IEndpointConventionBuilder MapFallbackToFile(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string pattern,
string filePath,
StaticFileOptions options)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (pattern == null)
@ -189,15 +189,15 @@ namespace Microsoft.AspNetCore.Builder
throw new ArgumentNullException(nameof(filePath));
}
return routes.MapFallback(pattern, CreateRequestDelegate(routes, filePath, options));
return endpoints.MapFallback(pattern, CreateRequestDelegate(endpoints, filePath, options));
}
private static RequestDelegate CreateRequestDelegate(
IEndpointRouteBuilder routes,
IEndpointRouteBuilder endpoints,
string filePath,
StaticFileOptions options = null)
{
var app = routes.CreateApplicationBuilder();
var app = endpoints.CreateApplicationBuilder();
app.Use(next => context =>
{
context.Request.Path = "/" + filePath;

View File

@ -35,15 +35,15 @@ namespace Microsoft.AspNetCore.StaticFiles
.Configure(app =>
{
var environment = app.ApplicationServices.GetRequiredService<IWebHostEnvironment>();
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.Map("/hello", context =>
endpoints.Map("/hello", context =>
{
return context.Response.WriteAsync("Hello, world!");
});
routes.MapFallbackToFile("default.html", new StaticFileOptions()
endpoints.MapFallbackToFile("default.html", new StaticFileOptions()
{
FileProvider = new PhysicalFileProvider(Path.Combine(environment.WebRootPath, "SubFolder")),
});
@ -83,14 +83,15 @@ namespace Microsoft.AspNetCore.StaticFiles
.UseWebRoot(AppContext.BaseDirectory)
.Configure(app =>
{
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.Map("/hello", context =>
endpoints.Map("/hello", context =>
{
return context.Response.WriteAsync("Hello, world!");
});
routes.MapFallbackToFile("/prefix/{*path:nonfile}", "TestDocument.txt");
endpoints.MapFallbackToFile("/prefix/{*path:nonfile}", "TestDocument.txt");
});
});

View File

@ -50,12 +50,14 @@ namespace Microsoft.AspNetCore.StaticFiles
public async Task Endpoint_PassesThrough()
{
var builder = new WebHostBuilder()
.ConfigureServices(services => services.AddSingleton(LoggerFactory))
.ConfigureServices(services => { services.AddSingleton(LoggerFactory); services.AddRouting(); })
.UseKestrel()
.UseWebRoot(AppContext.BaseDirectory)
.Configure(app =>
{
// Routing first => static files noops
app.UseRouting();
app.Use(next => context =>
{
// Assign an endpoint, this will make the default files noop.
@ -70,6 +72,8 @@ namespace Microsoft.AspNetCore.StaticFiles
});
app.UseStaticFiles();
app.UseEndpoints(endpoints => {});
});
using (var server = builder.Start(TestUrlHelper.GetTestUrl(ServerType.Kestrel)))

View File

@ -82,6 +82,8 @@ namespace Microsoft.AspNetCore.StaticFiles
var server = StaticFilesTestServer.Create(
app =>
{
app.UseRouting();
app.Use(next => context =>
{
// Assign an endpoint, this will make the default files noop.
@ -100,8 +102,10 @@ namespace Microsoft.AspNetCore.StaticFiles
RequestPath = new PathString(""),
FileProvider = fileProvider
});
app.UseEndpoints(endpoints => {});
},
services => services.AddDirectoryBrowser());
services => { services.AddDirectoryBrowser(); services.AddRouting(); });
var response = await server.CreateRequest("/SubFolder/").GetAsync();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);

View File

@ -96,6 +96,8 @@ namespace Microsoft.AspNetCore.StaticFiles
var server = StaticFilesTestServer.Create(
app =>
{
app.UseRouting();
app.Use(next => context =>
{
// Assign an endpoint, this will make the directory browser noop
@ -115,8 +117,10 @@ namespace Microsoft.AspNetCore.StaticFiles
RequestPath = new PathString(""),
FileProvider = fileProvider
});
app.UseEndpoints(endpoints => {});
},
services => services.AddDirectoryBrowser());
services => { services.AddDirectoryBrowser(); services.AddRouting(); });
var response = await server.CreateRequest("/").GetAsync();
Assert.Equal(HttpStatusCode.NotAcceptable, response.StatusCode);

View File

@ -141,25 +141,31 @@ namespace MusicStore
// Add static files to the request pipeline
app.UseStaticFiles();
// Add authentication to the request pipeline
// Add the endpoint routing matcher middleware to the request pipeline
app.UseRouting();
// Add cookie-based authentication to the request pipeline
app.UseAuthentication();
// Add MVC to the request pipeline
app.UseMvc(routes =>
// Add the authorization middleware to the request pipeline
app.UseAuthorization();
// Add endpoints to the request pipeline
app.UseEndpoints(endpoints =>
{
routes.MapRoute(
endpoints.MapControllerRoute(
name: "areaRoute",
template: "{area:exists}/{controller}/{action}",
pattern: "{area:exists}/{controller}/{action}",
defaults: new { action = "Index" });
routes.MapRoute(
endpoints.MapControllerRoute(
name: "default",
template: "{controller}/{action}/{id?}",
pattern: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
routes.MapRoute(
endpoints.MapControllerRoute(
name: "api",
template: "{controller}/{id?}");
pattern: "{controller}/{id?}");
});
//Populates the MusicStore sample data

View File

@ -181,25 +181,31 @@ namespace MusicStore
// Add static files to the request pipeline
app.UseStaticFiles();
// Add the endpoint routing matcher middleware to the request pipeline
app.UseRouting();
// Add cookie-based authentication to the request pipeline
app.UseAuthentication();
// Add MVC to the request pipeline
app.UseMvc(routes =>
// Add the authorization middleware to the request pipeline
app.UseAuthorization();
// Add endpoints to the request pipeline
app.UseEndpoints(endpoints =>
{
routes.MapRoute(
endpoints.MapControllerRoute(
name: "areaRoute",
template: "{area:exists}/{controller}/{action}",
pattern: "{area:exists}/{controller}/{action}",
defaults: new { action = "Index" });
routes.MapRoute(
endpoints.MapControllerRoute(
name: "default",
template: "{controller}/{action}/{id?}",
pattern: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
routes.MapRoute(
endpoints.MapControllerRoute(
name: "api",
template: "{controller}/{id?}");
pattern: "{controller}/{id?}");
});
//Populates the MusicStore sample data

View File

@ -195,25 +195,31 @@ namespace MusicStore
// Add static files to the request pipeline
app.UseStaticFiles();
// Add the endpoint routing matcher middleware to the request pipeline
app.UseRouting();
// Add cookie-based authentication to the request pipeline
app.UseAuthentication();
// Add MVC to the request pipeline
app.UseMvc(routes =>
// Add the authorization middleware to the request pipeline
app.UseAuthorization();
// Add endpoints to the request pipeline
app.UseEndpoints(endpoints =>
{
routes.MapRoute(
endpoints.MapControllerRoute(
name: "areaRoute",
template: "{area:exists}/{controller}/{action}",
pattern: "{area:exists}/{controller}/{action}",
defaults: new { action = "Index" });
routes.MapRoute(
endpoints.MapControllerRoute(
name: "default",
template: "{controller}/{action}/{id?}",
pattern: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
routes.MapRoute(
endpoints.MapControllerRoute(
name: "api",
template: "{controller}/{id?}");
pattern: "{controller}/{id?}");
});
//Populates the MusicStore sample data

View File

@ -142,22 +142,31 @@ namespace MusicStore
// Add static files to the request pipeline
app.UseStaticFiles();
// Add MVC to the request pipeline
app.UseMvc(routes =>
// Add the endpoint routing matcher middleware to the request pipeline
app.UseRouting();
// Add cookie-based authentication to the request pipeline
app.UseAuthentication();
// Add the authorization middleware to the request pipeline
app.UseAuthorization();
// Add endpoints to the request pipeline
app.UseEndpoints(endpoints =>
{
routes.MapRoute(
endpoints.MapControllerRoute(
name: "areaRoute",
template: "{area:exists}/{controller}/{action}",
pattern: "{area:exists}/{controller}/{action}",
defaults: new { action = "Index" });
routes.MapRoute(
endpoints.MapControllerRoute(
name: "default",
template: "{controller}/{action}/{id?}",
pattern: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
routes.MapRoute(
endpoints.MapControllerRoute(
name: "api",
template: "{controller}/{id?}");
pattern: "{controller}/{id?}");
});
//Populates the MusicStore sample data

View File

@ -140,22 +140,31 @@ namespace MusicStore
// Add static files to the request pipeline
app.UseStaticFiles();
// Add MVC to the request pipeline
app.UseMvc(routes =>
// Add the endpoint routing matcher middleware to the request pipeline
app.UseRouting();
// Add cookie-based authentication to the request pipeline
app.UseAuthentication();
// Add the authorization middleware to the request pipeline
app.UseAuthorization();
// Add endpoints to the request pipeline
app.UseEndpoints(endpoints =>
{
routes.MapRoute(
endpoints.MapControllerRoute(
name: "areaRoute",
template: "{area:exists}/{controller}/{action}",
pattern: "{area:exists}/{controller}/{action}",
defaults: new { action = "Index" });
routes.MapRoute(
endpoints.MapControllerRoute(
name: "default",
template: "{controller}/{action}/{id?}",
pattern: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
routes.MapRoute(
endpoints.MapControllerRoute(
name: "api",
template: "{controller}/{id?}");
pattern: "{controller}/{id?}");
});
//Populates the MusicStore sample data

View File

@ -5,14 +5,14 @@ namespace Microsoft.AspNetCore.Builder
{
public static partial class ControllerEndpointRouteBuilderExtensions
{
public static void MapAreaControllerRoute(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string name, string areaName, string pattern, object defaults = null, object constraints = null, object dataTokens = null) { }
public static void MapControllerRoute(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string name, string pattern, object defaults = null, object constraints = null, object dataTokens = null) { }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapControllers(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapDefaultControllerRoute(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes) { throw null; }
public static void MapFallbackToAreaController(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string action, string controller, string area) { }
public static void MapFallbackToAreaController(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string pattern, string action, string controller, string area) { }
public static void MapFallbackToController(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string action, string controller) { }
public static void MapFallbackToController(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string pattern, string action, string controller) { }
public static void MapAreaControllerRoute(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string name, string areaName, string pattern, object defaults = null, object constraints = null, object dataTokens = null) { }
public static void MapControllerRoute(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string name, string pattern, object defaults = null, object constraints = null, object dataTokens = null) { }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapControllers(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints) { throw null; }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapDefaultControllerRoute(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints) { throw null; }
public static void MapFallbackToAreaController(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string action, string controller, string area) { }
public static void MapFallbackToAreaController(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string pattern, string action, string controller, string area) { }
public static void MapFallbackToController(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string action, string controller) { }
public static void MapFallbackToController(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string pattern, string action, string controller) { }
}
public static partial class MvcApplicationBuilderExtensions
{

View File

@ -20,36 +20,36 @@ namespace Microsoft.AspNetCore.Builder
/// <summary>
/// Adds endpoints for controller actions to the <see cref="IEndpointRouteBuilder"/> without specifying any routes.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/>.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/>.</param>
/// <returns>An <see cref="IEndpointConventionBuilder"/> for endpoints associated with controller actions.</returns>
public static IEndpointConventionBuilder MapControllers(this IEndpointRouteBuilder routes)
public static IEndpointConventionBuilder MapControllers(this IEndpointRouteBuilder endpoints)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
EnsureControllerServices(routes);
EnsureControllerServices(endpoints);
return GetOrCreateDataSource(routes);
return GetOrCreateDataSource(endpoints);
}
/// <summary>
/// Adds endpoints for controller actions to the <see cref="IEndpointRouteBuilder"/> and adds the default route
/// <c>{controller=Home}/{action=Index}/{id?}</c>.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/>.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/>.</param>
/// <returns>An <see cref="IEndpointConventionBuilder"/> for endpoints associated with controller actions.</returns>
public static IEndpointConventionBuilder MapDefaultControllerRoute(this IEndpointRouteBuilder routes)
public static IEndpointConventionBuilder MapDefaultControllerRoute(this IEndpointRouteBuilder endpoints)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
EnsureControllerServices(routes);
EnsureControllerServices(endpoints);
var dataSource = GetOrCreateDataSource(routes);
var dataSource = GetOrCreateDataSource(endpoints);
dataSource.AddRoute(new ConventionalRouteEntry(
"default",
"{controller=Home}/{action=Index}/{id?}",
@ -65,7 +65,7 @@ namespace Microsoft.AspNetCore.Builder
/// with the given <paramref name="name"/>, <paramref name="pattern"/>,
/// <paramref name="defaults"/>, <paramref name="constraints"/>, and <paramref name="dataTokens"/>.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="name">The name of the route.</param>
/// <param name="pattern">The URL pattern of the route.</param>
/// <param name="defaults">
@ -81,21 +81,21 @@ namespace Microsoft.AspNetCore.Builder
/// values of the data tokens.
/// </param>
public static void MapControllerRoute(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string name,
string pattern,
object defaults = null,
object constraints = null,
object dataTokens = null)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
EnsureControllerServices(routes);
EnsureControllerServices(endpoints);
var dataSource = GetOrCreateDataSource(routes);
var dataSource = GetOrCreateDataSource(endpoints);
dataSource.AddRoute(new ConventionalRouteEntry(
name,
pattern,
@ -109,7 +109,7 @@ namespace Microsoft.AspNetCore.Builder
/// with the given <paramref name="name"/>, <paramref name="areaName"/>, <paramref name="pattern"/>,
/// <paramref name="defaults"/>, <paramref name="constraints"/>, and <paramref name="dataTokens"/>.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="name">The name of the route.</param>
/// <param name="areaName">The area name.</param>
/// <param name="pattern">The URL pattern of the route.</param>
@ -126,7 +126,7 @@ namespace Microsoft.AspNetCore.Builder
/// values of the data tokens.
/// </param>
public static void MapAreaControllerRoute(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string name,
string areaName,
string pattern,
@ -134,9 +134,9 @@ namespace Microsoft.AspNetCore.Builder
object constraints = null,
object dataTokens = null)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (string.IsNullOrEmpty(areaName))
@ -150,7 +150,7 @@ namespace Microsoft.AspNetCore.Builder
var constraintsDictionary = new RouteValueDictionary(constraints);
constraintsDictionary["area"] = constraintsDictionary["area"] ?? new StringRouteConstraint(areaName);
routes.MapControllerRoute(name, pattern, defaultsDictionary, constraintsDictionary, dataTokens);
endpoints.MapControllerRoute(name, pattern, defaultsDictionary, constraintsDictionary, dataTokens);
}
/// <summary>
@ -158,7 +158,7 @@ namespace Microsoft.AspNetCore.Builder
/// requests for non-file-names with the lowest possible priority. The request will be routed to a controller endpoint that
/// matches <paramref name="action"/>, and <paramref name="controller"/>.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="action">The action name.</param>
/// <param name="controller">The controller name.</param>
/// <remarks>
@ -184,13 +184,13 @@ namespace Microsoft.AspNetCore.Builder
/// </para>
/// </remarks>
public static void MapFallbackToController(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string action,
string controller)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (action == null)
@ -203,14 +203,14 @@ namespace Microsoft.AspNetCore.Builder
throw new ArgumentNullException(nameof(controller));
}
EnsureControllerServices(routes);
EnsureControllerServices(endpoints);
// Called for side-effect to make sure that the data source is registered.
GetOrCreateDataSource(routes);
GetOrCreateDataSource(endpoints);
// Maps a fallback endpoint with an empty delegate. This is OK because
// we don't expect the delegate to run.
routes.MapFallback(context => Task.CompletedTask).Add(b =>
endpoints.MapFallback(context => Task.CompletedTask).Add(b =>
{
// MVC registers a policy that looks for this metadata.
b.Metadata.Add(CreateDynamicControllerMetadata(action, controller, area: null));
@ -222,7 +222,7 @@ namespace Microsoft.AspNetCore.Builder
/// requests for non-file-names with the lowest possible priority. The request will be routed to a controller endpoint that
/// matches <paramref name="action"/>, and <paramref name="controller"/>.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="action">The action name.</param>
/// <param name="controller">The controller name.</param>
@ -252,14 +252,14 @@ namespace Microsoft.AspNetCore.Builder
/// </para>
/// </remarks>
public static void MapFallbackToController(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string pattern,
string action,
string controller)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (pattern == null)
@ -277,14 +277,14 @@ namespace Microsoft.AspNetCore.Builder
throw new ArgumentNullException(nameof(controller));
}
EnsureControllerServices(routes);
EnsureControllerServices(endpoints);
// Called for side-effect to make sure that the data source is registered.
GetOrCreateDataSource(routes);
GetOrCreateDataSource(endpoints);
// Maps a fallback endpoint with an empty delegate. This is OK because
// we don't expect the delegate to run.
routes.MapFallback(pattern, context => Task.CompletedTask).Add(b =>
endpoints.MapFallback(pattern, context => Task.CompletedTask).Add(b =>
{
// MVC registers a policy that looks for this metadata.
b.Metadata.Add(CreateDynamicControllerMetadata(action, controller, area: null));
@ -296,7 +296,7 @@ namespace Microsoft.AspNetCore.Builder
/// requests for non-file-names with the lowest possible priority. The request will be routed to a controller endpoint that
/// matches <paramref name="action"/>, <paramref name="controller"/>, and <paramref name="area"/>.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="action">The action name.</param>
/// <param name="controller">The controller name.</param>
/// <param name="area">The area name.</param>
@ -323,14 +323,14 @@ namespace Microsoft.AspNetCore.Builder
/// </para>
/// </remarks>
public static void MapFallbackToAreaController(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string action,
string controller,
string area)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (action == null)
@ -343,14 +343,14 @@ namespace Microsoft.AspNetCore.Builder
throw new ArgumentNullException(nameof(controller));
}
EnsureControllerServices(routes);
EnsureControllerServices(endpoints);
// Called for side-effect to make sure that the data source is registered.
GetOrCreateDataSource(routes);
GetOrCreateDataSource(endpoints);
// Maps a fallback endpoint with an empty delegate. This is OK because
// we don't expect the delegate to run.
routes.MapFallback(context => Task.CompletedTask).Add(b =>
endpoints.MapFallback(context => Task.CompletedTask).Add(b =>
{
// MVC registers a policy that looks for this metadata.
b.Metadata.Add(CreateDynamicControllerMetadata(action, controller, area));
@ -362,7 +362,7 @@ namespace Microsoft.AspNetCore.Builder
/// requests for non-file-names with the lowest possible priority. The request will be routed to a controller endpoint that
/// matches <paramref name="action"/>, <paramref name="controller"/>, and <paramref name="area"/>.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="action">The action name.</param>
/// <param name="controller">The controller name.</param>
@ -393,15 +393,15 @@ namespace Microsoft.AspNetCore.Builder
/// </para>
/// </remarks>
public static void MapFallbackToAreaController(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string pattern,
string action,
string controller,
string area)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (pattern == null)
@ -419,14 +419,14 @@ namespace Microsoft.AspNetCore.Builder
throw new ArgumentNullException(nameof(controller));
}
EnsureControllerServices(routes);
EnsureControllerServices(endpoints);
// Called for side-effect to make sure that the data source is registered.
GetOrCreateDataSource(routes);
GetOrCreateDataSource(endpoints);
// Maps a fallback endpoint with an empty delegate. This is OK because
// we don't expect the delegate to run.
routes.MapFallback(pattern, context => Task.CompletedTask).Add(b =>
endpoints.MapFallback(pattern, context => Task.CompletedTask).Add(b =>
{
// MVC registers a policy that looks for this metadata.
b.Metadata.Add(CreateDynamicControllerMetadata(action, controller, area));
@ -443,9 +443,9 @@ namespace Microsoft.AspNetCore.Builder
});
}
private static void EnsureControllerServices(IEndpointRouteBuilder routes)
private static void EnsureControllerServices(IEndpointRouteBuilder endpoints)
{
var marker = routes.ServiceProvider.GetService<MvcMarkerService>();
var marker = endpoints.ServiceProvider.GetService<MvcMarkerService>();
if (marker == null)
{
throw new InvalidOperationException(Resources.FormatUnableToFindServices(
@ -455,13 +455,13 @@ namespace Microsoft.AspNetCore.Builder
}
}
private static ControllerActionEndpointDataSource GetOrCreateDataSource(IEndpointRouteBuilder routes)
private static ControllerActionEndpointDataSource GetOrCreateDataSource(IEndpointRouteBuilder endpoints)
{
var dataSource = routes.DataSources.OfType<ControllerActionEndpointDataSource>().FirstOrDefault();
var dataSource = endpoints.DataSources.OfType<ControllerActionEndpointDataSource>().FirstOrDefault();
if (dataSource == null)
{
dataSource = routes.ServiceProvider.GetRequiredService<ControllerActionEndpointDataSource>();
routes.DataSources.Add(dataSource);
dataSource = endpoints.ServiceProvider.GetRequiredService<ControllerActionEndpointDataSource>();
endpoints.DataSources.Add(dataSource);
}
return dataSource;

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.AspNetCore.Mvc.Routing;
@ -18,9 +17,6 @@ namespace Microsoft.AspNetCore.Builder
/// </summary>
public static class MvcApplicationBuilderExtensions
{
// Property key set in routing package by UseEndpointRouting to indicate middleware is registered
private const string EndpointRoutingRegisteredKey = "__EndpointRoutingMiddlewareRegistered";
/// <summary>
/// Adds MVC to the <see cref="IApplicationBuilder"/> request execution pipeline.
/// </summary>
@ -88,58 +84,23 @@ namespace Microsoft.AspNetCore.Builder
if (options.Value.EnableEndpointRouting)
{
var dataSource = app.ApplicationServices.GetRequiredService<ActionEndpointDataSource>();
var endpointRouteBuilder = new EndpointRouteBuilder(app);
configureRoutes(endpointRouteBuilder);
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 entry = new ConventionalRouteEntry(
route.Name,
route.RouteTemplate,
route.Defaults,
route.Constraints.ToDictionary(kvp => kvp.Key, kvp => (object)kvp.Value),
route.DataTokens);
dataSource.AddRoute(entry);
}
else
{
throw new InvalidOperationException($"Cannot use '{router.GetType().FullName}' with Endpoint Routing.");
}
}
if (!app.Properties.TryGetValue(EndpointRoutingRegisteredKey, out _))
{
// Matching middleware has not been registered yet
// For back-compat register middleware so an endpoint is matched and then immediately used
app.UseRouting(routerBuilder =>
{
routerBuilder.DataSources.Add(dataSource);
});
}
return app.UseEndpoint();
var message =
"Endpoint Routing does not support 'IApplicationBuilder.UseMvc(...)'. To use " +
"'IApplicationBuilder.UseMvc' set 'MvcOptions.EnableEndpointRouting = false' inside " +
"'ConfigureServices(...).";
throw new InvalidOperationException(message);
}
else
var routes = new RouteBuilder(app)
{
var routes = new RouteBuilder(app)
{
DefaultHandler = app.ApplicationServices.GetRequiredService<MvcRouteHandler>(),
};
DefaultHandler = app.ApplicationServices.GetRequiredService<MvcRouteHandler>(),
};
configureRoutes(routes);
configureRoutes(routes);
routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(app.ApplicationServices));
routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(app.ApplicationServices));
return app.UseRouter(routes.Build());
}
return app.UseRouter(routes.Build());
}
private class EndpointRouteBuilder : IRouteBuilder

View File

@ -10,9 +10,6 @@ using Microsoft.AspNetCore.Mvc.Infrastructure;
namespace Microsoft.AspNetCore.Mvc.Routing
{
// This is only used to support the scenario where UseMvc is called with
// EnableEndpointRouting = true. For layering reasons we can't use the PageActionEndpointDataSource
// here.
internal class ActionEndpointDataSource : ActionEndpointDataSourceBase
{
private readonly ActionEndpointFactory _endpointFactory;
@ -22,10 +19,10 @@ namespace Microsoft.AspNetCore.Mvc.Routing
: base(actions)
{
_endpointFactory = endpointFactory;
_routes = new List<ConventionalRouteEntry>();
// IMPORTANT: this needs to be the last thing we do in the constructor.
// IMPORTANT: this needs to be the last thing we do in the constructor.
// Change notifications can happen immediately!
Subscribe();
}

View File

@ -62,7 +62,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Builder
}
[Fact]
public void UseMvc_EndpointRoutingEnabled_AddsRoute()
public void UseMvc_EndpointRoutingEnabled_ThrowsException()
{
// Arrange
var services = new ServiceCollection();
@ -73,21 +73,21 @@ namespace Microsoft.AspNetCore.Mvc.Core.Builder
var appBuilder = new ApplicationBuilder(serviceProvider);
// Act
appBuilder.UseMvc(routes =>
var ex = Assert.Throws<InvalidOperationException>(() =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
appBuilder.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
});
var routeOptions = appBuilder.ApplicationServices
.GetRequiredService<IOptions<RouteOptions>>();
var dataSource = (ActionEndpointDataSource)Assert.Single(routeOptions.Value.EndpointDataSources, ds => ds is ActionEndpointDataSource);
var endpointInfo = Assert.Single(dataSource.Routes);
Assert.Equal("default", endpointInfo.RouteName);
Assert.Equal("{controller=Home}/{action=Index}/{id?}", endpointInfo.Pattern.RawText);
var expected =
"Endpoint Routing does not support 'IApplicationBuilder.UseMvc(...)'. To use " +
"'IApplicationBuilder.UseMvc' set 'MvcOptions.EnableEndpointRouting = false' inside " +
"'ConfigureServices(...).";
Assert.Equal(expected, ex.Message);
}
}
}

View File

@ -5,11 +5,11 @@ namespace Microsoft.AspNetCore.Builder
{
public static partial class RazorPagesEndpointRouteBuilderExtensions
{
public static void MapFallbackToAreaPage(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string page, string area) { }
public static void MapFallbackToAreaPage(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string pattern, string page, string area) { }
public static void MapFallbackToPage(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string page) { }
public static void MapFallbackToPage(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes, string pattern, string page) { }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapRazorPages(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder routes) { throw null; }
public static void MapFallbackToAreaPage(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string page, string area) { }
public static void MapFallbackToAreaPage(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string pattern, string page, string area) { }
public static void MapFallbackToPage(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string page) { }
public static void MapFallbackToPage(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string pattern, string page) { }
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapRazorPages(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints) { throw null; }
}
}
namespace Microsoft.AspNetCore.Mvc.ApplicationModels

View File

@ -19,18 +19,18 @@ namespace Microsoft.AspNetCore.Builder
/// <summary>
/// Adds endpoints for Razor Pages to the <see cref="IEndpointRouteBuilder"/>.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/>.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/>.</param>
/// <returns>An <see cref="IEndpointConventionBuilder"/> for endpoints associated with Razor Pages.</returns>
public static IEndpointConventionBuilder MapRazorPages(this IEndpointRouteBuilder routes)
public static IEndpointConventionBuilder MapRazorPages(this IEndpointRouteBuilder endpoints)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
EnsureRazorPagesServices(routes);
EnsureRazorPagesServices(endpoints);
return GetOrCreateDataSource(routes);
return GetOrCreateDataSource(endpoints);
}
/// <summary>
@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.Builder
/// requests for non-file-names with the lowest possible priority. The request will be routed to a page endpoint that
/// matches <paramref name="page"/>.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="page">The page name.</param>
/// <remarks>
/// <para>
@ -57,11 +57,11 @@ namespace Microsoft.AspNetCore.Builder
/// will be available.
/// </para>
/// </remarks>
public static void MapFallbackToPage(this IEndpointRouteBuilder routes, string page)
public static void MapFallbackToPage(this IEndpointRouteBuilder endpoints, string page)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (page == null)
@ -71,14 +71,14 @@ namespace Microsoft.AspNetCore.Builder
PageConventionCollection.EnsureValidPageName(page, nameof(page));
EnsureRazorPagesServices(routes);
EnsureRazorPagesServices(endpoints);
// Called for side-effect to make sure that the data source is registered.
GetOrCreateDataSource(routes);
GetOrCreateDataSource(endpoints);
// Maps a fallback endpoint with an empty delegate. This is OK because
// we don't expect the delegate to run.
routes.MapFallback(context => Task.CompletedTask).Add(b =>
endpoints.MapFallback(context => Task.CompletedTask).Add(b =>
{
// MVC registers a policy that looks for this metadata.
b.Metadata.Add(CreateDynamicPageMetadata(page, area: null));
@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Builder
/// requests for non-file-names with the lowest possible priority. The request will be routed to a page endpoint that
/// matches <paramref name="page"/>.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="page">The action name.</param>
/// <remarks>
@ -114,13 +114,13 @@ namespace Microsoft.AspNetCore.Builder
/// </para>
/// </remarks>
public static void MapFallbackToPage(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string pattern,
string page)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (pattern == null)
@ -135,14 +135,14 @@ namespace Microsoft.AspNetCore.Builder
PageConventionCollection.EnsureValidPageName(page, nameof(page));
EnsureRazorPagesServices(routes);
EnsureRazorPagesServices(endpoints);
// Called for side-effect to make sure that the data source is registered.
GetOrCreateDataSource(routes);
GetOrCreateDataSource(endpoints);
// Maps a fallback endpoint with an empty delegate. This is OK because
// we don't expect the delegate to run.
routes.MapFallback(pattern, context => Task.CompletedTask).Add(b =>
endpoints.MapFallback(pattern, context => Task.CompletedTask).Add(b =>
{
// MVC registers a policy that looks for this metadata.
b.Metadata.Add(CreateDynamicPageMetadata(page, area: null));
@ -154,7 +154,7 @@ namespace Microsoft.AspNetCore.Builder
/// requests for non-file-names with the lowest possible priority. The request will be routed to a page endpoint that
/// matches <paramref name="page"/>, and <paramref name="area"/>.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="page">The action name.</param>
/// <param name="area">The area name.</param>
/// <remarks>
@ -175,13 +175,13 @@ namespace Microsoft.AspNetCore.Builder
/// </para>
/// </remarks>
public static void MapFallbackToAreaPage(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string page,
string area)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (page == null)
@ -191,14 +191,14 @@ namespace Microsoft.AspNetCore.Builder
PageConventionCollection.EnsureValidPageName(page, nameof(page));
EnsureRazorPagesServices(routes);
EnsureRazorPagesServices(endpoints);
// Called for side-effect to make sure that the data source is registered.
GetOrCreateDataSource(routes);
GetOrCreateDataSource(endpoints);
// Maps a fallback endpoint with an empty delegate. This is OK because
// we don't expect the delegate to run.
routes.MapFallback(context => Task.CompletedTask).Add(b =>
endpoints.MapFallback(context => Task.CompletedTask).Add(b =>
{
// MVC registers a policy that looks for this metadata.
b.Metadata.Add(CreateDynamicPageMetadata(page, area));
@ -210,7 +210,7 @@ namespace Microsoft.AspNetCore.Builder
/// requests for non-file-names with the lowest possible priority. The request will be routed to a page endpoint that
/// matches <paramref name="page"/>, and <paramref name="area"/>.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="pattern">The route pattern.</param>
/// <param name="page">The action name.</param>
/// <param name="area">The area name.</param>
@ -235,14 +235,14 @@ namespace Microsoft.AspNetCore.Builder
/// </para>
/// </remarks>
public static void MapFallbackToAreaPage(
this IEndpointRouteBuilder routes,
this IEndpointRouteBuilder endpoints,
string pattern,
string page,
string area)
{
if (routes == null)
if (endpoints == null)
{
throw new ArgumentNullException(nameof(routes));
throw new ArgumentNullException(nameof(endpoints));
}
if (pattern == null)
@ -257,14 +257,14 @@ namespace Microsoft.AspNetCore.Builder
PageConventionCollection.EnsureValidPageName(page, nameof(page));
EnsureRazorPagesServices(routes);
EnsureRazorPagesServices(endpoints);
// Called for side-effect to make sure that the data source is registered.
GetOrCreateDataSource(routes);
GetOrCreateDataSource(endpoints);
// Maps a fallback endpoint with an empty delegate. This is OK because
// we don't expect the delegate to run.
routes.MapFallback(pattern, context => Task.CompletedTask).Add(b =>
endpoints.MapFallback(pattern, context => Task.CompletedTask).Add(b =>
{
// MVC registers a policy that looks for this metadata.
b.Metadata.Add(CreateDynamicPageMetadata(page, area));
@ -280,9 +280,9 @@ namespace Microsoft.AspNetCore.Builder
});
}
private static void EnsureRazorPagesServices(IEndpointRouteBuilder routes)
private static void EnsureRazorPagesServices(IEndpointRouteBuilder endpoints)
{
var marker = routes.ServiceProvider.GetService<PageActionEndpointDataSource>();
var marker = endpoints.ServiceProvider.GetService<PageActionEndpointDataSource>();
if (marker == null)
{
throw new InvalidOperationException(Mvc.Core.Resources.FormatUnableToFindServices(
@ -292,13 +292,13 @@ namespace Microsoft.AspNetCore.Builder
}
}
private static PageActionEndpointDataSource GetOrCreateDataSource(IEndpointRouteBuilder routes)
private static PageActionEndpointDataSource GetOrCreateDataSource(IEndpointRouteBuilder endpoints)
{
var dataSource = routes.DataSources.OfType<PageActionEndpointDataSource>().FirstOrDefault();
var dataSource = endpoints.DataSources.OfType<PageActionEndpointDataSource>().FirstOrDefault();
if (dataSource == null)
{
dataSource = routes.ServiceProvider.GetRequiredService<PageActionEndpointDataSource>();
routes.DataSources.Add(dataSource);
dataSource = endpoints.ServiceProvider.GetRequiredService<PageActionEndpointDataSource>();
endpoints.DataSources.Add(dataSource);
}
return dataSource;

View File

@ -155,8 +155,15 @@ namespace BasicApi
}
});
app.UseRouting();
app.UseAuthentication();
app.UseMvc();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
private void CreateDatabaseTables(IServiceProvider services)

View File

@ -116,7 +116,16 @@ namespace BasicViews
});
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
private void CreateDatabaseTables(IServiceProvider services)

View File

@ -22,7 +22,13 @@ public class Startup
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}
private static List<DataA> DataA = GenerateDataA();

View File

@ -38,7 +38,8 @@ namespace MvcSandbox
app.UseDeveloperExceptionPage();
app.UseStaticFiles();
app.UseRouting(builder =>
app.UseRouting();
app.UseEndpoints(builder =>
{
builder.MapGet(
requestDelegate: WriteEndpoints,
@ -73,8 +74,6 @@ namespace MvcSandbox
builder.MapComponentHub<MvcSandbox.Components.App>("app");
builder.MapFallbackToPage("/Components");
});
app.UseEndpoint();
}
private static Task WriteEndpoints(HttpContext httpContext)

View File

@ -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.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class GlobalAuthorizationFilterEndpointRoutingTest : GlobalAuthorizationFilterTestBase, IClassFixture<MvcTestFixture<SecurityWebSite.StartupWithGlobalDenyAnonymousFilter>>
{
public GlobalAuthorizationFilterEndpointRoutingTest(MvcTestFixture<SecurityWebSite.StartupWithGlobalDenyAnonymousFilter> fixture)
{
Factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder);
Client = Factory.CreateDefaultClient();
}
private static void ConfigureWebHostBuilder(IWebHostBuilder builder) =>
builder.UseStartup<SecurityWebSite.StartupWithGlobalDenyAnonymousFilter>();
public WebApplicationFactory<SecurityWebSite.StartupWithGlobalDenyAnonymousFilter> Factory { get; }
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/8387")]
public override Task DeniesAnonymousUsers_ByDefault()
{
return Task.CompletedTask;
}
}
}

View File

@ -5,29 +5,16 @@ using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class GlobalAuthorizationFilterTest : IClassFixture<MvcTestFixture<SecurityWebSite.StartupWithGlobalDenyAnonymousFilter>>
public abstract class GlobalAuthorizationFilterTestBase : IClassFixture<MvcTestFixture<SecurityWebSite.StartupWithGlobalDenyAnonymousFilter>>
{
public GlobalAuthorizationFilterTest(MvcTestFixture<SecurityWebSite.StartupWithGlobalDenyAnonymousFilter> fixture)
{
Factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder);
Client = Factory.CreateDefaultClient();
}
private static void ConfigureWebHostBuilder(IWebHostBuilder builder) =>
builder.UseStartup<SecurityWebSite.StartupWithGlobalDenyAnonymousFilter>();
public HttpClient Client { get; }
public WebApplicationFactory<SecurityWebSite.StartupWithGlobalDenyAnonymousFilter> Factory { get; }
public HttpClient Client { get; protected set; }
[Fact]
public async Task DeniesAnonymousUsers_ByDefault()
public virtual async Task DeniesAnonymousUsers_ByDefault()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/Administration/Index");

View File

@ -0,0 +1,24 @@
// 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.Linq;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class GlobalAuthorizationFilterUseMvcTest : GlobalAuthorizationFilterTestBase, IClassFixture<MvcTestFixture<SecurityWebSite.StartupWithGlobalDenyAnonymousFilterWithUseMvc>>
{
public GlobalAuthorizationFilterUseMvcTest(MvcTestFixture<SecurityWebSite.StartupWithGlobalDenyAnonymousFilterWithUseMvc> fixture)
{
Factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder);
Client = Factory.CreateDefaultClient();
}
private static void ConfigureWebHostBuilder(IWebHostBuilder builder) =>
builder.UseStartup<SecurityWebSite.StartupWithGlobalDenyAnonymousFilterWithUseMvc>();
public WebApplicationFactory<SecurityWebSite.StartupWithGlobalDenyAnonymousFilterWithUseMvc> Factory { get; }
}
}

View File

@ -918,7 +918,7 @@ Hello from /Pages/WithViewStart/Index.cshtml!";
{
// Arrange
var expected =
@"Microsoft.AspNetCore.Mvc.Routing.EndpointRoutingUrlHelper
@"Microsoft.AspNetCore.Mvc.Routing.UrlHelper
Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper`1[AspNetCore.InjectedPageProperties]
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[AspNetCore.InjectedPageProperties]";

View File

@ -93,7 +93,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
Assert.Equal("Hello from /Admin/RouteTemplate my-user-id 4", content.Trim());
}
[Fact]
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/8387")]
public async Task AuthConvention_IsAppliedOnBasePathRelativePaths_ForFiles()
{
// Act
@ -104,7 +104,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
Assert.Equal("/Login?ReturnUrl=%2FConventions%2FAuth", response.Headers.Location.PathAndQuery);
}
[Fact]
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/8387")]
public async Task AuthConvention_IsAppliedOnBasePathRelativePaths_For_Folders()
{
// Act
@ -372,7 +372,7 @@ Hello from /Pages/Shared/";
Assert.Equal(expected, response.Trim(), ignoreLineEndingDifferences: true);
}
[Fact]
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/8387")]
public async Task AuthorizeFolderConvention_CanBeAppliedToAreaPages()
{
// Act

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public HttpClient Client { get; }
[Fact]
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/8387")]
public async Task Authorize_AppliedUsingConvention_Works()
{
// Act
@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
Assert.Equal("Hello from Anonymous", content.Trim());
}
[Fact]
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/8387")]
public async Task Authorize_AppliedUsingAttributeOnModel_Works()
{
// Act

View File

@ -1,386 +0,0 @@
// 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.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class RoutingUseMvcWithEndpointRoutingTest : RoutingTestsBase<RoutingWebSite.StartupWithUseMvcAndEndpointRouting>
{
public RoutingUseMvcWithEndpointRoutingTest(MvcTestFixture<RoutingWebSite.StartupWithUseMvcAndEndpointRouting> fixture)
: base(fixture)
{
}
[Fact]
public async Task AttributeRoutedAction_ContainsPage_RouteMatched()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/PageRoute/Attribute/pagevalue");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Contains("/PageRoute/Attribute/pagevalue", result.ExpectedUrls);
Assert.Equal("PageRoute", result.Controller);
Assert.Equal("AttributeRoute", result.Action);
Assert.Contains(
new KeyValuePair<string, object>("page", "pagevalue"),
result.RouteValues);
}
[Fact]
public async Task ParameterTransformer_TokenReplacement_Found()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/parameter-transformer/my-action");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("ParameterTransformer", result.Controller);
Assert.Equal("MyAction", result.Action);
}
[Fact]
public async Task ParameterTransformer_TokenReplacement_NotFound()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/ParameterTransformer/MyAction");
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task AttributeRoutedAction_Parameters_Found()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/EndpointRouting/Index");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("EndpointRouting", result.Controller);
Assert.Equal("Index", result.Action);
}
[Fact]
public async Task AttributeRoutedAction_Parameters_DefaultValue_Found()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/EndpointRouting");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("EndpointRouting", result.Controller);
Assert.Equal("Index", result.Action);
}
[Fact]
public async Task AttributeRoutedAction_ParameterTransformer_Found()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/endpoint-routing/ParameterTransformer");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("EndpointRouting", result.Controller);
Assert.Equal("ParameterTransformer", result.Action);
}
[Fact]
public async Task AttributeRoutedAction_ParameterTransformer_NotFound()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/EndpointRouting/ParameterTransformer");
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task AttributeRoutedAction_ParameterTransformer_LinkToSelf()
{
// Arrange
var url = LinkFrom("http://localhost/endpoint-routing/ParameterTransformer").To(new { });
// Act
var response = await Client.GetAsync(url);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("EndpointRouting", result.Controller);
Assert.Equal("ParameterTransformer", result.Action);
Assert.Equal("/endpoint-routing/ParameterTransformer", result.Link);
}
[Fact]
public async Task AttributeRoutedAction_ParameterTransformer_LinkWithAmbientController()
{
// Arrange
var url = LinkFrom("http://localhost/endpoint-routing/ParameterTransformer").To(new { action = "Get", id = 5 });
// Act
var response = await Client.GetAsync(url);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("EndpointRouting", result.Controller);
Assert.Equal("ParameterTransformer", result.Action);
Assert.Equal("/endpoint-routing/5", result.Link);
}
[Fact]
public async Task AttributeRoutedAction_ParameterTransformer_LinkToAttributeRoutedController()
{
// Arrange
var url = LinkFrom("http://localhost/endpoint-routing/ParameterTransformer").To(new { action = "ShowPosts", controller = "Blog" });
// Act
var response = await Client.GetAsync(url);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("EndpointRouting", result.Controller);
Assert.Equal("ParameterTransformer", result.Action);
Assert.Equal("/Blog/ShowPosts", result.Link);
}
[Fact]
public async Task AttributeRoutedAction_ParameterTransformer_LinkToConventionalController()
{
// Arrange
var url = LinkFrom("http://localhost/endpoint-routing/ParameterTransformer").To(new { action = "Index", controller = "Home" });
// Act
var response = await Client.GetAsync(url);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("EndpointRouting", result.Controller);
Assert.Equal("ParameterTransformer", result.Action);
Assert.Equal("/", result.Link);
}
[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<bool>(body);
Assert.True(result);
}
[Fact]
public async override Task RouteData_Routers_ConventionalRoute()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/RouteData/Conventional");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<ResultData>(body);
Assert.Equal(
Array.Empty<string>(),
result.Routers);
}
[Fact]
public async override Task RouteData_Routers_AttributeRoute()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/RouteData/Attribute");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<ResultData>(body);
Assert.Equal(
Array.Empty<string>(),
result.Routers);
}
// Endpoint 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);
}
[Fact]
public async Task ConventionalRoutedAction_ParameterTransformer()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/ConventionalTransformerRoute/conventional-transformer/Index");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("ConventionalTransformer", result.Controller);
Assert.Equal("Index", result.Action);
}
[Fact]
public async Task ConventionalRoutedAction_ParameterTransformer_NotFound()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/ConventionalTransformerRoute/ConventionalTransformer/Index");
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task ConventionalRoutedAction_ParameterTransformer_DefaultValue()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/ConventionalTransformerRoute/conventional-transformer");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("ConventionalTransformer", result.Controller);
Assert.Equal("Index", result.Action);
}
[Fact]
public async Task ConventionalRoutedAction_ParameterTransformer_WithParam()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/ConventionalTransformerRoute/conventional-transformer/Param/my-value");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("ConventionalTransformer", result.Controller);
Assert.Equal("Param", result.Action);
Assert.Equal("/ConventionalTransformerRoute/conventional-transformer/Param/my-value", Assert.Single(result.ExpectedUrls));
}
[Fact]
public async Task ConventionalRoutedAction_ParameterTransformer_LinkToConventionalController()
{
// Arrange
var url = LinkFrom("http://localhost/ConventionalTransformerRoute/conventional-transformer/Index").To(new { action = "Index", controller = "Home" });
// Act
var response = await Client.GetAsync(url);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("ConventionalTransformer", result.Controller);
Assert.Equal("Index", result.Action);
Assert.Equal("/", result.Link);
}
[Fact]
public async Task ConventionalRoutedAction_ParameterTransformer_LinkToConventionalControllerWithParam()
{
// Arrange
var url = LinkFrom("http://localhost/ConventionalTransformerRoute/conventional-transformer/Index").To(new { action = "Param", controller = "ConventionalTransformer", param = "MyValue" });
// Act
var response = await Client.GetAsync(url);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("ConventionalTransformer", result.Controller);
Assert.Equal("Index", result.Action);
Assert.Equal("/ConventionalTransformerRoute/conventional-transformer/Param/my-value", result.Link);
}
[Fact]
public async Task ConventionalRoutedAction_ParameterTransformer_LinkToSelf()
{
// Arrange
var url = LinkFrom("http://localhost/ConventionalTransformerRoute/conventional-transformer/Index").To(new {});
// Act
var response = await Client.GetAsync(url);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("ConventionalTransformer", result.Controller);
Assert.Equal("Index", result.Action);
Assert.Equal("/ConventionalTransformerRoute/conventional-transformer", result.Link);
}
// Endpoint routing exposes HTTP 405s for HTTP method mismatches.
protected override void AssertCorsRejectionStatusCode(HttpResponseMessage response)
{
Assert.Equal(HttpStatusCode.MethodNotAllowed, response.StatusCode);
}
}
}

View File

@ -46,9 +46,10 @@ namespace ApiExplorerWebSite
public void Configure(IApplicationBuilder app)
{
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapDefaultControllerRoute();
endpoints.MapDefaultControllerRoute();
});
}

View File

@ -27,12 +27,13 @@ namespace ApplicationModelWebSite
public void Configure(IApplicationBuilder app)
{
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapControllerRoute(name: "areaRoute", pattern: "{area:exists}/{controller=Home}/{action=Index}");
routes.MapControllerRoute(name: "default", pattern: "{controller}/{action}/{id?}");
endpoints.MapControllerRoute(name: "areaRoute", pattern: "{area:exists}/{controller=Home}/{action=Index}");
endpoints.MapControllerRoute(name: "default", pattern: "{controller}/{action}/{id?}");
routes.MapRazorPages();
endpoints.MapRazorPages();
});
}

View File

@ -35,14 +35,15 @@ namespace BasicWebSite
// Initializes the RequestId service for each request
app.UseMiddleware<RequestIdMiddleware>();
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapControllerRoute(
endpoints.MapControllerRoute(
name: "ActionAsMethod",
pattern: "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" });
routes.MapControllerRoute(
endpoints.MapControllerRoute(
name: "PageRoute",
pattern: "{controller}/{action}/{page}");
});

View File

@ -39,10 +39,11 @@ namespace BasicWebSite
return next();
});
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapDefaultControllerRoute();
routes.MapRazorPages();
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}

View File

@ -28,11 +28,11 @@ namespace BasicWebSite
app.UseDeveloperExceptionPage();
app.UseCookiePolicy();
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapDefaultControllerRoute();
routes.MapRazorPages();
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}
}

View File

@ -48,9 +48,10 @@ namespace BasicWebSite
{
app.UseDeveloperExceptionPage();
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapControllers();
endpoints.MapControllers();
});
}
}

View File

@ -26,10 +26,11 @@ namespace BasicWebSite
app.UseDeveloperExceptionPage();
app.UseSession();
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapDefaultControllerRoute();
routes.MapRazorPages();
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}
}

View File

@ -64,9 +64,10 @@ namespace ControllersFromServicesWebSite
public void Configure(IApplicationBuilder app)
{
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapDefaultControllerRoute();
endpoints.MapDefaultControllerRoute();
});
}

View File

@ -74,9 +74,10 @@ namespace CorsWebSite
public virtual void Configure(IApplicationBuilder app)
{
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapControllers();
endpoints.MapControllers();
});
}

View File

@ -22,9 +22,10 @@ namespace ErrorPageMiddlewareWebSite
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapControllers();
endpoints.MapControllers();
});
}

View File

@ -16,4 +16,5 @@ type Startup () =
member this.Configure(app: IApplicationBuilder) =
app.UseDeveloperExceptionPage() |> ignore
app.UseStaticFiles() |> ignore
app.UseMvcWithDefaultRoute() |> ignore
app.UseRouting() |> ignore
app.UseEndpoints(fun endpoints -> endpoints.MapDefaultControllerRoute() |> ignore) |> ignore

View File

@ -21,9 +21,10 @@ namespace FilesWebSite
public void Configure(IApplicationBuilder app)
{
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapDefaultControllerRoute();
endpoints.MapDefaultControllerRoute();
});
}

View File

@ -26,9 +26,10 @@ namespace FormatterWebSite
public void Configure(IApplicationBuilder app)
{
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapDefaultControllerRoute();
endpoints.MapDefaultControllerRoute();
});
}
}

View File

@ -23,9 +23,10 @@ namespace FormatterWebSite
public void Configure(IApplicationBuilder app)
{
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapDefaultControllerRoute();
endpoints.MapDefaultControllerRoute();
});
}
}

View File

@ -19,9 +19,10 @@ namespace FormatterWebSite
public void Configure(IApplicationBuilder app)
{
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapDefaultControllerRoute();
endpoints.MapDefaultControllerRoute();
});
}
}

View File

@ -40,17 +40,18 @@ namespace GenericHostWebSite
app.UseStaticFiles();
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapControllerRoute(
endpoints.MapControllerRoute(
"areaRoute",
"{area:exists}/{controller}/{action}",
new { controller = "Home", action = "Index" });
routes.MapControllerRoute("ActionAsMethod", "{controller}/{action}",
endpoints.MapControllerRoute("ActionAsMethod", "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" });
routes.MapControllerRoute("PageRoute", "{controller}/{action}/{page}");
endpoints.MapControllerRoute("PageRoute", "{controller}/{action}/{page}");
});
}
}

View File

@ -30,22 +30,23 @@ namespace HtmlGenerationWebSite
{
app.UseStaticFiles();
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapControllerRoute(
endpoints.MapControllerRoute(
name: "areaRoute",
pattern: "{area:exists}/{controller}/{action}/{id?}",
defaults: new { action = "Index" });
routes.MapControllerRoute(
endpoints.MapControllerRoute(
name: "productRoute",
pattern: "Product/{action}",
defaults: new { controller = "Product" });
routes.MapControllerRoute(
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action}/{id?}",
defaults: new { controller = "HtmlGeneration_Home", action = "Index" });
routes.MapRazorPages();
endpoints.MapRazorPages();
});
}

View File

@ -23,10 +23,11 @@ namespace RazorBuildWebSite
public void Configure(IApplicationBuilder app)
{
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapDefaultControllerRoute();
routes.MapRazorPages();
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}

View File

@ -25,13 +25,16 @@ namespace RazorPagesWebSite
public void Configure(IApplicationBuilder app)
{
app.UseRouting(routes =>
{
routes.MapControllers();
routes.MapRazorPages();
});
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapRazorPages();
});
}
}
}

View File

@ -38,14 +38,17 @@ namespace RazorPagesWebSite
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseStaticFiles();
app.UseRouting(routes =>
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
routes.MapControllerRoute("areaRoute", "{area:exists}/{controller=Home}/{action=Index}");
routes.MapRazorPages();
endpoints.MapControllerRoute("areaRoute", "{area:exists}/{controller=Home}/{action=Index}");
endpoints.MapRazorPages();
});
}
}

View File

@ -15,7 +15,7 @@ namespace RazorPagesWebSite
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options => options.LoginPath = "/Login");
services.AddMvc()
services.AddMvc(options => options.EnableEndpointRouting = false)
.AddMvcLocalization()
.AddNewtonsoftJson()
.AddRazorPagesOptions(options =>

View File

@ -45,6 +45,9 @@ namespace RazorWebSite
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();
app.UseRouting();
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("en-GB", "en-US"),
@ -62,10 +65,10 @@ namespace RazorWebSite
}
});
app.UseRouting(routes =>
app.UseEndpoints(endpoints =>
{
routes.MapDefaultControllerRoute();
routes.MapRazorPages();
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}
}

View File

@ -32,6 +32,10 @@ namespace RazorWebSite
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();
app.UseStaticFiles();
app.UseRouting();
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("en-US", "en-US"),
@ -44,11 +48,10 @@ namespace RazorWebSite
new CultureInfo("en-US")
}
});
app.UseStaticFiles();
app.UseRouting(routes =>
app.UseEndpoints(endpoints =>
{
routes.MapDefaultControllerRoute();
endpoints.MapDefaultControllerRoute();
});
}
}

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>

View File

@ -38,62 +38,63 @@ namespace RoutingWebSite
public virtual void Configure(IApplicationBuilder app)
{
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapControllerRoute(
endpoints.MapControllerRoute(
"NonParameterConstraintRoute",
"NonParameterConstraintRoute/{controller}/{action}",
defaults: null,
constraints: new { controller = "NonParameterConstraint", nonParameter = new QueryStringConstraint() });
routes.MapControllerRoute(
endpoints.MapControllerRoute(
"DataTokensRoute",
"DataTokensRoute/{controller}/{action}",
defaults: null,
constraints: new { controller = "DataTokens" },
dataTokens: new { hasDataTokens = true });
routes.MapControllerRoute(
endpoints.MapControllerRoute(
"ConventionalTransformerRoute",
"ConventionalTransformerRoute/{controller:slugify}/{action=Index}/{param:slugify?}",
defaults: null,
constraints: new { controller = "ConventionalTransformer" });
routes.MapControllerRoute(
endpoints.MapControllerRoute(
"DefaultValuesRoute_OptionalParameter",
"DefaultValuesRoute/Optional/{controller=DEFAULTVALUES}/{action=OPTIONALPARAMETER}/{id?}/{**catchAll}",
defaults: null,
constraints: new { controller = "DefaultValues", action = "OptionalParameter" });
routes.MapControllerRoute(
endpoints.MapControllerRoute(
"DefaultValuesRoute_DefaultParameter",
"DefaultValuesRoute/Default/{controller=DEFAULTVALUES}/{action=DEFAULTPARAMETER}/{id=17}/{**catchAll}",
defaults: null,
constraints: new { controller = "DefaultValues", action = "DefaultParameter" });
routes.MapAreaControllerRoute(
endpoints.MapAreaControllerRoute(
"flightRoute",
"adminRoute",
"{area:exists}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index" },
constraints: new { area = "Travel" });
routes.MapControllerRoute(
endpoints.MapControllerRoute(
"PageRoute",
"{controller}/{action}/{page}",
defaults: null,
constraints: new { controller = "PageRoute" });
routes.MapControllerRoute(
endpoints.MapControllerRoute(
"ActionAsMethod",
"{controller}/{action}",
defaults: new { controller = "Home", action = "Index" });
routes.MapControllerRoute(
endpoints.MapControllerRoute(
"RouteWithOptionalSegment",
"{controller}/{action}/{path?}");
routes.MapRazorPages();
endpoints.MapRazorPages();
});
app.Map("/afterrouting", b => b.Run(c =>

View File

@ -25,15 +25,16 @@ namespace RoutingWebSite
public void Configure(IApplicationBuilder app)
{
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
// Workaround for #8130
//
// You can't fallback to this unless it already has another route.
routes.MapAreaControllerRoute("admin", "Admin", "Admin/{controller=Home}/{action=Index}/{id?}");
endpoints.MapAreaControllerRoute("admin", "Admin", "Admin/{controller=Home}/{action=Index}/{id?}");
routes.MapFallbackToAreaController("admin/{*path:nonfile}", "Index", "Fallback", "Admin");
routes.MapFallbackToPage("/FallbackPage");
endpoints.MapFallbackToAreaController("admin/{*path:nonfile}", "Index", "Fallback", "Admin");
endpoints.MapFallbackToPage("/FallbackPage");
});
app.Map("/afterrouting", b => b.Run(c =>

View File

@ -39,10 +39,11 @@ namespace RoutingWebSite
public void Configure(IApplicationBuilder app)
{
app.UseRouting(routes =>
app.UseRouting();
app.UseEndpoints(endpoints =>
{
routes.MapDefaultControllerRoute();
routes.MapRazorPages();
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}
}

View File

@ -1,80 +0,0 @@
// 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.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace RoutingWebSite
{
public class StartupWithUseMvcAndEndpointRouting : Startup
{
public override void Configure(IApplicationBuilder app)
{
app.UseMvc(routes =>
{
routes.MapRoute(
"NonParameterConstraintRoute",
"NonParameterConstraintRoute/{controller}/{action}",
defaults: null,
constraints: new { controller = "NonParameterConstraint", nonParameter = new QueryStringConstraint() });
routes.MapRoute(
"DataTokensRoute",
"DataTokensRoute/{controller}/{action}",
defaults: null,
constraints: new { controller = "DataTokens" },
dataTokens: new { hasDataTokens = true });
routes.MapRoute(
"ConventionalTransformerRoute",
"ConventionalTransformerRoute/{controller:slugify}/{action=Index}/{param:slugify?}",
defaults: null,
constraints: new { controller = "ConventionalTransformer" });
routes.MapRoute(
"DefaultValuesRoute_OptionalParameter",
"DefaultValuesRoute/Optional/{controller=DEFAULTVALUES}/{action=OPTIONALPARAMETER}/{id?}/{**catchAll}",
defaults: null,
constraints: new { controller = "DefaultValues", action = "OptionalParameter" });
routes.MapRoute(
"DefaultValuesRoute_DefaultParameter",
"DefaultValuesRoute/Default/{controller=DEFAULTVALUES}/{action=DEFAULTPARAMETER}/{id=17}/{**catchAll}",
defaults: null,
constraints: new { controller = "DefaultValues", action = "DefaultParameter" });
routes.MapAreaRoute(
"flightRoute",
"adminRoute",
"{area:exists}/{controller}/{action}",
defaults: new { controller = "Home", action = "Index" },
constraints: new { area = "Travel" });
routes.MapRoute(
"PageRoute",
"{controller}/{action}/{page}",
defaults: null,
constraints: new { controller = "PageRoute" });
routes.MapRoute(
"ActionAsMethod",
"{controller}/{action}",
defaults: new { controller = "Home", action = "Index" });
routes.MapRoute(
"RouteWithOptionalSegment",
"{controller}/{action}/{path?}");
});
app.Map("/afterrouting", b => b.Run(c =>
{
return c.Response.WriteAsync("Hello from middleware after routing");
}));
}
}
}

View File

@ -18,7 +18,7 @@ namespace SecurityWebSite
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Latest);
services.AddAntiforgery();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
options.LoginPath = "/Home/Login";
options.LogoutPath = "/Home/Logout";
@ -30,11 +30,14 @@ namespace SecurityWebSite
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseRouting();
app.UseRouting(routes =>
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
routes.MapDefaultControllerRoute();
endpoints.MapDefaultControllerRoute();
});
}
}

View File

@ -33,11 +33,14 @@ namespace SecurityWebSite
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseRouting();
app.UseRouting(routes =>
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
routes.MapDefaultControllerRoute();
endpoints.MapDefaultControllerRoute();
});
}
}

View File

@ -0,0 +1,42 @@
// 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.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization.Policy;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Extensions.DependencyInjection;
namespace SecurityWebSite
{
public class StartupWithGlobalDenyAnonymousFilterWithUseMvc
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/Home/Login";
options.LogoutPath = "/Home/Logout";
}).AddCookie("Cookie2");
services.AddMvc(o =>
{
o.EnableEndpointRouting = false;
o.Filters.Add(new AuthorizeFilter());
})
.SetCompatibilityVersion(CompatibilityVersion.Latest);
services.AddScoped<IPolicyEvaluator, CountingPolicyEvaluator>();
}
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseMvcWithDefaultRoute();
}
}
}

View File

@ -0,0 +1,37 @@
// 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.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization.Policy;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
namespace SecurityWebSite
{
public class StartupWithUseMvc
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc(options => options.EnableEndpointRouting = false)
.SetCompatibilityVersion(CompatibilityVersion.Latest);
services.AddAntiforgery();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
options.LoginPath = "/Home/Login";
options.LogoutPath = "/Home/Logout";
}).AddCookie("Cookie2");
services.AddScoped<IPolicyEvaluator, CountingPolicyEvaluator>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseMvcWithDefaultRoute();
}
}
}

Some files were not shown because too many files have changed in this diff Show More