diff --git a/src/Microsoft.AspNetCore.Http/Internal/ApplicationBuilder.cs b/src/Microsoft.AspNetCore.Http/Internal/ApplicationBuilder.cs index d0b6b6f6bf..291ccc3893 100644 --- a/src/Microsoft.AspNetCore.Http/Internal/ApplicationBuilder.cs +++ b/src/Microsoft.AspNetCore.Http/Internal/ApplicationBuilder.cs @@ -81,6 +81,13 @@ 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 (endpointRequestDelegate != null) + { + return endpointRequestDelegate(context); + } + context.Response.StatusCode = 404; return Task.CompletedTask; }; diff --git a/test/Microsoft.AspNetCore.Http.Tests/Internal/ApplicationBuilderTests.cs b/test/Microsoft.AspNetCore.Http.Tests/Internal/ApplicationBuilderTests.cs index e1336c82ba..cee2042aae 100644 --- a/test/Microsoft.AspNetCore.Http.Tests/Internal/ApplicationBuilderTests.cs +++ b/test/Microsoft.AspNetCore.Http.Tests/Internal/ApplicationBuilderTests.cs @@ -1,7 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; using Xunit; namespace Microsoft.AspNetCore.Builder.Internal @@ -20,6 +22,59 @@ namespace Microsoft.AspNetCore.Builder.Internal Assert.Equal(404, httpContext.Response.StatusCode); } + [Fact] + public void BuildImplicitlyCallsMatchedEndpointAsLastStep() + { + var builder = new ApplicationBuilder(null); + var app = builder.Build(); + + var endpointCalled = false; + var endpoint = new Endpoint( + context => + { + endpointCalled = true; + return Task.CompletedTask; + }, + EndpointMetadataCollection.Empty, + "Test endpoint"); + + var httpContext = new DefaultHttpContext(); + httpContext.SetEndpoint(endpoint); + + app.Invoke(httpContext); + + Assert.True(endpointCalled); + } + + [Fact] + public void BuildDoesNotCallMatchedEndpointWhenTerminated() + { + var builder = new ApplicationBuilder(null); + builder.Use((context, next) => + { + // Do not call next + return Task.CompletedTask; + }); + var app = builder.Build(); + + var endpointCalled = false; + var endpoint = new Endpoint( + context => + { + endpointCalled = true; + return Task.CompletedTask; + }, + EndpointMetadataCollection.Empty, + "Test endpoint"); + + var httpContext = new DefaultHttpContext(); + httpContext.SetEndpoint(endpoint); + + app.Invoke(httpContext); + + Assert.False(endpointCalled); + } + [Fact] public void PropertiesDictionaryIsDistinctAfterNew() {