-
Notifications
You must be signed in to change notification settings - Fork 10.7k
Support SkipStatusCodePages on endpoints and authorized routes #38509
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| // 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; | ||
|
|
||
| /// <summary> | ||
| /// Defines a contract used to specify metadata for skipping the StatusCodePage | ||
| /// middleware in <see cref="Endpoint.Metadata"/>. | ||
| /// </summary> | ||
| public interface ISkipStatusCodePagesMetadata | ||
| { | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| #nullable enable | ||
| *REMOVED*abstract Microsoft.AspNetCore.Http.HttpResponse.ContentType.get -> string! | ||
| abstract Microsoft.AspNetCore.Http.HttpResponse.ContentType.get -> string? | ||
| Microsoft.AspNetCore.Http.Metadata.ISkipStatusCodePagesMetadata |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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,6 +42,13 @@ public async Task Invoke(HttpContext context) | |||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| var statusCodeFeature = new StatusCodePagesFeature(); | ||||||||||||||||||||||||
| context.Features.Set<IStatusCodePagesFeature>(statusCodeFeature); | ||||||||||||||||||||||||
| var endpoint = context.GetEndpoint(); | ||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Having This could be after
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a fair consideration, and we would want to make sure the doc was accurate. Do we have examples of anything else that's endpoint aware but placed before UseRouting? This ordering was recommended so that the attribute behavior could be applied early and then overridden by other middleware/code while processing the request. That's consistent with how the filter works today. If placed after _next, you wouldn't be able to tell if IStatusCodePagesFeature had the default value or if it was set by something in the app.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Tratcher Sorry, I'm confused a little bit! Are you saying that going forward
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. StatusCodePagesMiddleware can stay where it is and continue to work for existing apps, but if you want it to be route aware and use the SkipStatusCodePages attribute then it will need to be placed after routing.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh I see! The implementation has been changed after I add the original comment. It's all clear now. I assume if StatusCodePagesMiddleware with re-execute would be used after routing it will automatically add the routing, right? aspnetcore/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesExtensions.cs Lines 179 to 189 in 4656f15
What if public class StatusCodePagesFeature : IStatusCodePagesFeature
{
private readonly HttpContext httpContext;
public StatusCodePagesFeature(HttpContext httpContext)
{
_httpContext = httpContext;
}
private bool? _enabled;
public bool Enabled
{
get
{
if (_enabled == null)
{
var endpoint = _httpContext.GetEndpoint();
var skipStatusCodePageMetadata = endpoint?.Metadata.GetMetadata<ISkipStatusCodePagesMetadata>();
if (skipStatusCodePageMetadata is not null)
{
_enabled = false;
}
else
{
_enabled = true;
}
}
return _enabled;
}
set
{
_enabled = value;
}
}
}
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. StatusCodePagesFeature is a public class, so if we were to change it, we'd have to make it continue to work as it does today when called with the empty constructor. I think it might be worthwhile to just use a new internal IStatusCodePagesFeature that lazily reads the endpoint. The only thing I don't like is locking in the wrong value for Enabled if the property is read before UseRouting(). You could definitely argue it's no worse than today where it's locked in immediately when the feature is added, but that feels a little less surprising. I wonder if it'd be better to avoid negative caching unless the property is explicitly set, but maybe that's even more surprising. |
||||||||||||||||||||||||
| var skipStatusCodePageMetadata = endpoint?.Metadata.GetMetadata<ISkipStatusCodePagesMetadata>(); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if (skipStatusCodePageMetadata is not null) | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| statusCodeFeature.Enabled = false; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| await _next(context); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we add a test that verifies we let people change the feature in the body of the endpoint?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Except verify that you can set it to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems fair! I missed seeing this before the auto-merge kicked it but I'll address in a follow-up. |
||
| { | ||
| 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); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we drop the
in Endpoint.Metadata? I don't know if it helps clarify things?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the
in Endpoint.Metadata. What's wrong with it? It could point the curious to API docs for endpoint routing.