Skip to content

Commit d612592

Browse files
committed
Use IStatusCodeReExecuteFeature.
1 parent 3e77c62 commit d612592

File tree

8 files changed

+39
-31
lines changed

8 files changed

+39
-31
lines changed

src/Components/Endpoints/src/Microsoft.AspNetCore.Components.Endpoints.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
<Reference Include="Microsoft.AspNetCore.Antiforgery" />
4141
<Reference Include="Microsoft.AspNetCore.Components.Authorization" />
4242
<Reference Include="Microsoft.AspNetCore.Components.Web" />
43+
<Reference Include="Microsoft.AspNetCore.Diagnostics" />
4344
<Reference Include="Microsoft.AspNetCore.Diagnostics.Abstractions" />
4445
<Reference Include="Microsoft.AspNetCore.DataProtection.Extensions" />
4546
<Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />

src/Components/Endpoints/src/RazorComponentEndpointInvoker.cs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,15 @@ private async Task RenderComponentCore(HttpContext context)
3939
{
4040
context.Response.ContentType = RazorComponentResultExecutor.DefaultContentType;
4141
var isErrorHandler = context.Features.Get<IExceptionHandlerFeature>() is not null;
42-
var hasStatusCodePage = context.Features.Get<IStatusCodePagesFeature>() is not null;
42+
var isReExecuted = context.Features.Get<IStatusCodeReExecuteFeature>() is not null;
4343
if (isErrorHandler)
4444
{
4545
Log.InteractivityDisabledForErrorHandling(_logger);
4646
}
47-
_renderer.InitializeStreamingRenderingFraming(context, isErrorHandler, hasStatusCodePage);
48-
bool avoidEditingHeaders = hasStatusCodePage && context.Response.StatusCode == StatusCodes.Status404NotFound;
49-
if (!avoidEditingHeaders)
47+
_renderer.InitializeStreamingRenderingFraming(context, isErrorHandler, isReExecuted);
48+
if (!isReExecuted)
5049
{
50+
// re-executed pages have Headers already set up
5151
EndpointHtmlRenderer.MarkAsAllowingEnhancedNavigation(context);
5252
}
5353

@@ -91,7 +91,7 @@ await _renderer.InitializeStandardComponentServicesAsync(
9191
using var bufferWriter = new BufferedTextWriter(writer);
9292

9393
int originalStatusCode = context.Response.StatusCode;
94-
bool isErrorHandlerOrHasStatusCodePage = isErrorHandler || hasStatusCodePage;
94+
bool isErrorHandlerOrReExecuted = isErrorHandler || isReExecuted;
9595

9696
// Note that we always use Static rendering mode for the top-level output from a RazorComponentResult,
9797
// because you never want to serialize the invocation of RazorComponentResultHost. Instead, that host
@@ -100,14 +100,15 @@ await _renderer.InitializeStandardComponentServicesAsync(
100100
context,
101101
rootComponent,
102102
ParameterView.Empty,
103-
waitForQuiescence: result.IsPost || isErrorHandlerOrHasStatusCodePage);
103+
waitForQuiescence: result.IsPost || isErrorHandlerOrReExecuted);
104104

105-
bool requiresReexecution = originalStatusCode != context.Response.StatusCode && hasStatusCodePage;
106-
if (requiresReexecution)
105+
bool isReExecutionRequested = context.Features.Get<IStatusCodeReExecuteFeature>() is not null;
106+
bool avoidStartingResponse = !isReExecuted && isReExecutionRequested;
107+
if (avoidStartingResponse)
107108
{
108-
// If the response is a 404, we don't want to write any content.
109-
// This is because the 404 status code is used by the routing middleware
110-
// to indicate that no endpoint was found for the request.
109+
// re-execution feature was set during rendering,
110+
// we should finish early to avoid writing to the response
111+
// and let the re-execution middleware take care of it
111112
return;
112113
}
113114

@@ -162,7 +163,7 @@ await _renderer.InitializeStandardComponentServicesAsync(
162163
}
163164

164165
// Emit comment containing state.
165-
if (!isErrorHandlerOrHasStatusCodePage)
166+
if (!isErrorHandlerOrReExecuted)
166167
{
167168
var componentStateHtmlContent = await _renderer.PrerenderPersistedStateAsync(context);
168169
componentStateHtmlContent.WriteTo(bufferWriter, HtmlEncoder.Default);

src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.EventDispatch.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Microsoft.AspNetCore.Components.Endpoints.Rendering;
55
using Microsoft.AspNetCore.Components.Rendering;
66
using Microsoft.AspNetCore.Components.RenderTree;
7+
using Microsoft.AspNetCore.Diagnostics;
78
using Microsoft.AspNetCore.Http;
89
using Microsoft.AspNetCore.WebUtilities;
910
using Microsoft.Extensions.DependencyInjection;
@@ -93,6 +94,8 @@ private async Task SetNotFoundResponseAsync(string baseUri)
9394
_httpContext.Response.StatusCode = StatusCodes.Status404NotFound;
9495
_httpContext.Response.ContentType = null;
9596
}
97+
var statusCodeFeature = new StatusCodeReExecuteFeature();
98+
_httpContext.Features.Set<IStatusCodeReExecuteFeature>(statusCodeFeature);
9699
SignalRendererToFinishRendering();
97100
}
98101

src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.Prerendering.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ internal partial class EndpointHtmlRenderer
1919

2020
protected override IComponent ResolveComponentForRenderMode([DynamicallyAccessedMembers(Component)] Type componentType, int? parentComponentId, IComponentActivator componentActivator, IComponentRenderMode renderMode)
2121
{
22-
if (_isHandlingErrors || _hasStatusCodePage)
22+
if (_isHandlingErrors || _isReExecuted)
2323
{
2424
// Ignore the render mode boundary in error scenarios.
2525
return componentActivator.CreateInstance(componentType);
@@ -167,7 +167,7 @@ private async Task WaitForResultReady(bool waitForQuiescence, PrerenderedCompone
167167
}
168168
else if (_nonStreamingPendingTasks.Count > 0)
169169
{
170-
if (_hasStatusCodePage)
170+
if (_isReExecuted)
171171
{
172172
HandleNonStreamingTasks();
173173
}

src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.Streaming.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,13 @@ internal partial class EndpointHtmlRenderer
2323
private HashSet<int>? _visitedComponentIdsInCurrentStreamingBatch;
2424
private string? _ssrFramingCommentMarkup;
2525
private bool _isHandlingErrors;
26-
private bool _hasStatusCodePage;
26+
private bool _isReExecuted;
2727

28-
public void InitializeStreamingRenderingFraming(HttpContext httpContext, bool isErrorHandler, bool hasStatusCodePage)
28+
public void InitializeStreamingRenderingFraming(HttpContext httpContext, bool isErrorHandler, bool isReExecuted)
2929
{
3030
_isHandlingErrors = isErrorHandler;
31-
_hasStatusCodePage = hasStatusCodePage;
32-
bool avoidEditingHeaders = hasStatusCodePage && httpContext.Response.StatusCode == StatusCodes.Status404NotFound;
33-
if (!avoidEditingHeaders && IsProgressivelyEnhancedNavigation(httpContext.Request))
31+
_isReExecuted = isReExecuted;
32+
if (!isReExecuted && IsProgressivelyEnhancedNavigation(httpContext.Request))
3433
{
3534
var id = Guid.NewGuid().ToString();
3635
httpContext.Response.Headers.Add(_streamingRenderingFramingHeaderName, id);

src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ protected override ComponentState CreateComponentState(int componentId, ICompone
165165

166166
protected override void AddPendingTask(ComponentState? componentState, Task task)
167167
{
168-
if (_hasStatusCodePage)
168+
if (_isReExecuted)
169169
{
170170
return;
171171
}

src/Components/Endpoints/src/Results/RazorComponentResultExecutor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ private static Task RenderComponentToResponse(
4848
return endpointHtmlRenderer.Dispatcher.InvokeAsync(async () =>
4949
{
5050
var isErrorHandler = httpContext.Features.Get<IExceptionHandlerFeature>() is not null;
51-
var hasStatusCodePage = httpContext.Features.Get<IStatusCodePagesFeature>() is not null;
52-
endpointHtmlRenderer.InitializeStreamingRenderingFraming(httpContext, isErrorHandler, hasStatusCodePage);
51+
var isReExecuted = httpContext.Features.Get<IStatusCodeReExecuteFeature>() is not null;
52+
endpointHtmlRenderer.InitializeStreamingRenderingFraming(httpContext, isErrorHandler, isReExecuted);
5353
EndpointHtmlRenderer.MarkAsAllowingEnhancedNavigation(httpContext);
5454

5555
// We could pool these dictionary instances if we wanted, and possibly even the ParameterView

src/Components/test/E2ETest/ServerRenderingTests/StatusCodePagesTest.cs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
using System.Linq;
77
using System.Text;
88
using System.Threading.Tasks;
9-
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
10-
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
11-
using TestServer;
129
using Components.TestServer.RazorComponents;
10+
using Components.TestServer.RazorComponents.Pages.StreamingRendering;
11+
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
12+
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
1313
using Microsoft.AspNetCore.E2ETesting;
14-
using Xunit.Abstractions;
1514
using OpenQA.Selenium;
15+
using TestServer;
16+
using Xunit.Abstractions;
1617

1718
namespace Microsoft.AspNetCore.Components.E2ETests.ServerRenderingTests;
1819

@@ -21,16 +22,19 @@ public class StatusCodePagesTest(BrowserFixture browserFixture, BasicTestAppServ
2122
{
2223

2324
[Theory]
24-
[InlineData(true)]
25-
[InlineData(false)]
26-
public void StatusCodePagesWithReexecution(bool setNotFound)
25+
[InlineData(false, true)]
26+
[InlineData(true, false)]
27+
[InlineData(false, false)]
28+
public void StatusCodePagesWithReExecution(bool setNotFound, bool streaming)
2729
{
28-
Navigate($"{ServerPathBase}/reexecution/set-not-found?shouldSet={setNotFound}");
30+
string streamingPath = streaming ? "streaming-" : "";
31+
Navigate($"{ServerPathBase}/reexecution/{streamingPath}set-not-found?shouldSet={setNotFound}");
2932

3033
string expectedTitle = setNotFound ? "Re-executed page" : "Original page";
3134
Browser.Equal(expectedTitle, () => Browser.Title);
3235
var infoText = Browser.FindElement(By.Id("test-info")).Text;
33-
string expectedInfoText = setNotFound ? "Welcome On Page Re-executed After Not Found Event" : "Any content";
36+
// streaming when response started does not re-execute
37+
string expectedInfoText = streaming ? "Default Not Found Page" : setNotFound ? "Welcome On Page Re-executed After Not Found Event" : "Any content";
3438
Assert.Contains(expectedInfoText, infoText);
3539
}
3640

0 commit comments

Comments
 (0)