From 40b66e742061c39704a99ae46cdd18673c546e7d Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Thu, 18 Nov 2021 21:56:31 +0000 Subject: [PATCH 1/3] Support SkipStatusCodePages on endpoints and authorized routes --- .../src/Metadata/IStatusCodePagesMetadata.cs | 16 +++++++++ .../src/PublicAPI.Unshipped.txt | 2 ++ .../StatusCodePagesMiddleware.cs | 5 ++- ...rosoft.AspNetCore.Diagnostics.Tests.csproj | 1 + .../UnitTests/StatusCodeMiddlewareTest.cs | 35 +++++++++++++++++++ .../src/PublicAPI.Unshipped.txt | 1 + .../src/SkipStatusCodePagesAttribute.cs | 15 +++++++- .../test/SkipStatusCodePagesAttributeTest.cs | 4 +++ 8 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 src/Http/Http.Abstractions/src/Metadata/IStatusCodePagesMetadata.cs diff --git a/src/Http/Http.Abstractions/src/Metadata/IStatusCodePagesMetadata.cs b/src/Http/Http.Abstractions/src/Metadata/IStatusCodePagesMetadata.cs new file mode 100644 index 000000000000..d63052387371 --- /dev/null +++ b/src/Http/Http.Abstractions/src/Metadata/IStatusCodePagesMetadata.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http.Metadata; + +/// +/// Defines a contract used to specify metadata for skipping the StatusCodePage +/// middleware in . +/// +public interface ISkipStatusCodePagesMetadata +{ + /// + /// Gets whether or not status code pages are enabled for an endpoint. + /// + bool Enabled { get; } +} diff --git a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt index 244ddbf827dc..4d7e903b6f1e 100644 --- a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt @@ -1,3 +1,5 @@ #nullable enable *REMOVED*abstract Microsoft.AspNetCore.Http.HttpResponse.ContentType.get -> string! abstract Microsoft.AspNetCore.Http.HttpResponse.ContentType.get -> string? +Microsoft.AspNetCore.Http.Metadata.ISkipStatusCodePagesMetadata +Microsoft.AspNetCore.Http.Metadata.ISkipStatusCodePagesMetadata.Enabled.get -> bool diff --git a/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesMiddleware.cs b/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesMiddleware.cs index 8e47ff9ca221..341a9cc2f7be 100644 --- a/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesMiddleware.cs +++ b/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesMiddleware.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Metadata; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Diagnostics; @@ -41,10 +42,12 @@ public async Task Invoke(HttpContext context) { var statusCodeFeature = new StatusCodePagesFeature(); context.Features.Set(statusCodeFeature); + var endpoint = context.GetEndpoint(); + var statusCodeMetadata = endpoint?.Metadata.GetMetadata(); await _next(context); - if (!statusCodeFeature.Enabled) + if (!statusCodeFeature.Enabled || statusCodeMetadata?.Enabled is false) { // Check if the feature is still available because other middleware (such as a web API written in MVC) could // have disabled the feature to prevent HTML status code responses from showing up to an API client. diff --git a/src/Middleware/Diagnostics/test/UnitTests/Microsoft.AspNetCore.Diagnostics.Tests.csproj b/src/Middleware/Diagnostics/test/UnitTests/Microsoft.AspNetCore.Diagnostics.Tests.csproj index 8a7999f7eeb0..b0a4f171d50f 100644 --- a/src/Middleware/Diagnostics/test/UnitTests/Microsoft.AspNetCore.Diagnostics.Tests.csproj +++ b/src/Middleware/Diagnostics/test/UnitTests/Microsoft.AspNetCore.Diagnostics.Tests.csproj @@ -20,6 +20,7 @@ + diff --git a/src/Middleware/Diagnostics/test/UnitTests/StatusCodeMiddlewareTest.cs b/src/Middleware/Diagnostics/test/UnitTests/StatusCodeMiddlewareTest.cs index cc43acc02650..0bec738dab0d 100644 --- a/src/Middleware/Diagnostics/test/UnitTests/StatusCodeMiddlewareTest.cs +++ b/src/Middleware/Diagnostics/test/UnitTests/StatusCodeMiddlewareTest.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -283,4 +284,38 @@ public async Task Reexecute_WorksAfterUseRoutingWithGlobalRouteBuilder() var content = await response.Content.ReadAsStringAsync(); Assert.Equal("errorPage", content); } + + [Fact] + public async Task SkipStatusCodePages_SupportsEndpoints() + { + var builder = WebApplication.CreateBuilder(); + builder.WebHost.UseTestServer(); + await using var app = builder.Build(); + + app.UseRouting(); + + app.UseStatusCodePages(); + + app.UseEndpoints(endpoints => + { + endpoints.MapGet("/", [SkipStatusCodePages] (c) => + { + c.Response.StatusCode = 404; + return Task.CompletedTask; + }); + }); + + app.Run((context) => + { + throw new InvalidOperationException("Invalid input provided."); + }); + + await app.StartAsync(); + + using var server = app.GetTestServer(); + var client = server.CreateClient(); + var response = await client.GetAsync("/"); + var content = await response.Content.ReadAsStringAsync(); + Assert.Empty(content); + } } diff --git a/src/Mvc/Mvc.ViewFeatures/src/PublicAPI.Unshipped.txt b/src/Mvc/Mvc.ViewFeatures/src/PublicAPI.Unshipped.txt index 7dc5c58110bf..d5109c634579 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/PublicAPI.Unshipped.txt +++ b/src/Mvc/Mvc.ViewFeatures/src/PublicAPI.Unshipped.txt @@ -1 +1,2 @@ #nullable enable +Microsoft.AspNetCore.Mvc.SkipStatusCodePagesAttribute.Enabled.get -> bool diff --git a/src/Mvc/Mvc.ViewFeatures/src/SkipStatusCodePagesAttribute.cs b/src/Mvc/Mvc.ViewFeatures/src/SkipStatusCodePagesAttribute.cs index 78cd16c7ca08..e7136633b16e 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/SkipStatusCodePagesAttribute.cs +++ b/src/Mvc/Mvc.ViewFeatures/src/SkipStatusCodePagesAttribute.cs @@ -4,6 +4,7 @@ using System; using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Http.Metadata; namespace Microsoft.AspNetCore.Mvc; @@ -11,8 +12,17 @@ namespace Microsoft.AspNetCore.Mvc; /// A filter that prevents execution of the StatusCodePages middleware. /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] -public class SkipStatusCodePagesAttribute : Attribute, IResourceFilter +public class SkipStatusCodePagesAttribute : Attribute, IResourceFilter, ISkipStatusCodePagesMetadata { + /// + /// Initializes a new instace of + /// with set to . + /// + public SkipStatusCodePagesAttribute() + { + Enabled = false; + } + /// public void OnResourceExecuted(ResourceExecutedContext context) { @@ -33,4 +43,7 @@ public void OnResourceExecuting(ResourceExecutingContext context) statusCodeFeature.Enabled = false; } } + + /// + public bool Enabled { get; } } diff --git a/src/Mvc/Mvc.ViewFeatures/test/SkipStatusCodePagesAttributeTest.cs b/src/Mvc/Mvc.ViewFeatures/test/SkipStatusCodePagesAttributeTest.cs index 599c110c7e8b..04134466f0c6 100644 --- a/src/Mvc/Mvc.ViewFeatures/test/SkipStatusCodePagesAttributeTest.cs +++ b/src/Mvc/Mvc.ViewFeatures/test/SkipStatusCodePagesAttributeTest.cs @@ -28,6 +28,7 @@ public void SkipStatusCodePagesAttribute_TurnsOfStatusCodePages() // Assert Assert.False(statusCodePagesFeature.Enabled); + Assert.False(skipStatusCodeAttribute.Enabled); } [Fact] @@ -39,6 +40,9 @@ public void SkipStatusCodePagesAttribute_Does_Not_Throw_If_Feature_Missing() // Act skipStatusCodeAttribute.OnResourceExecuting(resourceExecutingContext); + + // Assert + Assert.False(skipStatusCodeAttribute.Enabled); } private static ResourceExecutingContext CreateResourceExecutingContext(IFilterMetadata[] filters) From 6606f0fb72d4e5f21a44ea243e310b2d30e290d0 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Mon, 22 Nov 2021 23:04:41 +0000 Subject: [PATCH 2/3] Address feedback from API review --- .../src/Metadata/IStatusCodePagesMetadata.cs | 4 ---- .../Http.Abstractions/src/PublicAPI.Unshipped.txt | 1 - .../src/StatusCodePage/StatusCodePagesMiddleware.cs | 9 +++++++-- src/Mvc/Mvc.ViewFeatures/src/PublicAPI.Unshipped.txt | 1 - .../src/SkipStatusCodePagesAttribute.cs | 12 ------------ .../test/SkipStatusCodePagesAttributeTest.cs | 4 ---- 6 files changed, 7 insertions(+), 24 deletions(-) diff --git a/src/Http/Http.Abstractions/src/Metadata/IStatusCodePagesMetadata.cs b/src/Http/Http.Abstractions/src/Metadata/IStatusCodePagesMetadata.cs index d63052387371..1594b3da76e0 100644 --- a/src/Http/Http.Abstractions/src/Metadata/IStatusCodePagesMetadata.cs +++ b/src/Http/Http.Abstractions/src/Metadata/IStatusCodePagesMetadata.cs @@ -9,8 +9,4 @@ namespace Microsoft.AspNetCore.Http.Metadata; /// public interface ISkipStatusCodePagesMetadata { - /// - /// Gets whether or not status code pages are enabled for an endpoint. - /// - bool Enabled { get; } } diff --git a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt index 4d7e903b6f1e..945b4e263fab 100644 --- a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt @@ -2,4 +2,3 @@ *REMOVED*abstract Microsoft.AspNetCore.Http.HttpResponse.ContentType.get -> string! abstract Microsoft.AspNetCore.Http.HttpResponse.ContentType.get -> string? Microsoft.AspNetCore.Http.Metadata.ISkipStatusCodePagesMetadata -Microsoft.AspNetCore.Http.Metadata.ISkipStatusCodePagesMetadata.Enabled.get -> bool diff --git a/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesMiddleware.cs b/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesMiddleware.cs index 341a9cc2f7be..0e91ccdbf84b 100644 --- a/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesMiddleware.cs +++ b/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesMiddleware.cs @@ -43,11 +43,16 @@ public async Task Invoke(HttpContext context) var statusCodeFeature = new StatusCodePagesFeature(); context.Features.Set(statusCodeFeature); var endpoint = context.GetEndpoint(); - var statusCodeMetadata = endpoint?.Metadata.GetMetadata(); + var skipStatusCodePageMetadata = endpoint?.Metadata.GetMetadata(); + + if (skipStatusCodePageMetadata is not null) + { + statusCodeFeature.Enabled = false; + } await _next(context); - if (!statusCodeFeature.Enabled || statusCodeMetadata?.Enabled is false) + if (!statusCodeFeature.Enabled) { // Check if the feature is still available because other middleware (such as a web API written in MVC) could // have disabled the feature to prevent HTML status code responses from showing up to an API client. diff --git a/src/Mvc/Mvc.ViewFeatures/src/PublicAPI.Unshipped.txt b/src/Mvc/Mvc.ViewFeatures/src/PublicAPI.Unshipped.txt index d5109c634579..7dc5c58110bf 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/PublicAPI.Unshipped.txt +++ b/src/Mvc/Mvc.ViewFeatures/src/PublicAPI.Unshipped.txt @@ -1,2 +1 @@ #nullable enable -Microsoft.AspNetCore.Mvc.SkipStatusCodePagesAttribute.Enabled.get -> bool diff --git a/src/Mvc/Mvc.ViewFeatures/src/SkipStatusCodePagesAttribute.cs b/src/Mvc/Mvc.ViewFeatures/src/SkipStatusCodePagesAttribute.cs index e7136633b16e..113ac12888fb 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/SkipStatusCodePagesAttribute.cs +++ b/src/Mvc/Mvc.ViewFeatures/src/SkipStatusCodePagesAttribute.cs @@ -14,15 +14,6 @@ namespace Microsoft.AspNetCore.Mvc; [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class SkipStatusCodePagesAttribute : Attribute, IResourceFilter, ISkipStatusCodePagesMetadata { - /// - /// Initializes a new instace of - /// with set to . - /// - public SkipStatusCodePagesAttribute() - { - Enabled = false; - } - /// public void OnResourceExecuted(ResourceExecutedContext context) { @@ -43,7 +34,4 @@ public void OnResourceExecuting(ResourceExecutingContext context) statusCodeFeature.Enabled = false; } } - - /// - public bool Enabled { get; } } diff --git a/src/Mvc/Mvc.ViewFeatures/test/SkipStatusCodePagesAttributeTest.cs b/src/Mvc/Mvc.ViewFeatures/test/SkipStatusCodePagesAttributeTest.cs index 04134466f0c6..599c110c7e8b 100644 --- a/src/Mvc/Mvc.ViewFeatures/test/SkipStatusCodePagesAttributeTest.cs +++ b/src/Mvc/Mvc.ViewFeatures/test/SkipStatusCodePagesAttributeTest.cs @@ -28,7 +28,6 @@ public void SkipStatusCodePagesAttribute_TurnsOfStatusCodePages() // Assert Assert.False(statusCodePagesFeature.Enabled); - Assert.False(skipStatusCodeAttribute.Enabled); } [Fact] @@ -40,9 +39,6 @@ public void SkipStatusCodePagesAttribute_Does_Not_Throw_If_Feature_Missing() // Act skipStatusCodeAttribute.OnResourceExecuting(resourceExecutingContext); - - // Assert - Assert.False(skipStatusCodeAttribute.Enabled); } private static ResourceExecutingContext CreateResourceExecutingContext(IFilterMetadata[] filters) From b87b5517013774567821eace4aa597d26d8e900c Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Mon, 22 Nov 2021 23:27:14 +0000 Subject: [PATCH 3/3] Fix sort of usings --- src/Mvc/Mvc.ViewFeatures/src/SkipStatusCodePagesAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mvc/Mvc.ViewFeatures/src/SkipStatusCodePagesAttribute.cs b/src/Mvc/Mvc.ViewFeatures/src/SkipStatusCodePagesAttribute.cs index 113ac12888fb..6ee2da2f59bf 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/SkipStatusCodePagesAttribute.cs +++ b/src/Mvc/Mvc.ViewFeatures/src/SkipStatusCodePagesAttribute.cs @@ -3,8 +3,8 @@ using System; using Microsoft.AspNetCore.Diagnostics; -using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Http.Metadata; +using Microsoft.AspNetCore.Mvc.Filters; namespace Microsoft.AspNetCore.Mvc;