Skip to content

Unify the UX of template projects on navigation to non-existing page #62067

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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1411,4 +1411,41 @@ public void NavigatesWithInteractivityByRequestRedirection(bool controlFlowByExc
Browser.Click(By.Id("redirectButton"));
Browser.Equal("Routing test cases", () => Browser.Exists(By.Id("test-info")).Text);
}

[Theory]
// prerendering (SSR) is tested in NoInteractivityTest
[InlineData("ServerNonPrerendered")]
[InlineData("WebAssemblyNonPrerendered")]
public void ProgrammaticNavigationToNotExistingPathReExecutesTo404(string renderMode)
{
Navigate($"{ServerPathBase}/reexecution/redirection-not-found?renderMode={renderMode}&navigate-programmatically=true");
Assert404ReExecuted();
}

[Theory]
// prerendering (SSR) is tested in NoInteractivityTest
[InlineData("ServerNonPrerendered")]
[InlineData("WebAssemblyNonPrerendered")]
public void LinkNavigationToNotExistingPathReExecutesTo404(string renderMode)
{
Navigate($"{ServerPathBase}/reexecution/redirection-not-found?renderMode={renderMode}");
Browser.Click(By.Id("link-to-not-existing-page"));
Assert404ReExecuted();
}

[Theory]
// prerendering (SSR) is tested in NoInteractivityTest
[InlineData("ServerNonPrerendered")]
[InlineData("WebAssemblyNonPrerendered")]
public void BrowserNavigationToNotExistingPathReExecutesTo404(string renderMode)
{
// non-existing path has to have re-execution middleware set up
// so it has to have "reexecution" prefix. Otherwise middleware mapping
// will not be activated, see configuration in Startup
Navigate($"{ServerPathBase}/reexecution/not-existing-page?renderMode={renderMode}");
Assert404ReExecuted();
}

private void Assert404ReExecuted() =>
Browser.Equal("Welcome On Page Re-executed After Not Found Event", () => Browser.Exists(By.Id("test-info")).Text);
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,43 @@ public void CanRenderNotFoundPageAfterStreamingStarted()
Browser.Equal("Default Not Found Page", () => Browser.Title);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void ProgrammaticNavigationToNotExistingPathReExecutesTo404(bool streaming)
{
string streamingPath = streaming ? "-streaming" : "";
Navigate($"{ServerPathBase}/reexecution/redirection-not-found-ssr{streamingPath}?navigate-programmatically=true");
Assert404ReExecuted();
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void LinkNavigationToNotExistingPathReExecutesTo404(bool streaming)
{
string streamingPath = streaming ? "-streaming" : "";
Navigate($"{ServerPathBase}/reexecution/redirection-not-found-ssr{streamingPath}");
Browser.Click(By.Id("link-to-not-existing-page"));
Assert404ReExecuted();
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void BrowserNavigationToNotExistingPathReExecutesTo404(bool streaming)
{
// non-existing path has to have re-execution middleware set up
// so it has to have "reexecution" prefix. Otherwise middleware mapping
// will not be activated, see configuration in Startup
string streamingPath = streaming ? "-streaming" : "";
Navigate($"{ServerPathBase}/reexecution/not-existing-page-ssr{streamingPath}");
Assert404ReExecuted();
}

private void Assert404ReExecuted() =>
Browser.Equal("Welcome On Page Re-executed After Not Found Event", () => Browser.Exists(By.Id("test-info")).Text);

[Theory]
[InlineData(true)]
[InlineData(false)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,37 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

app.Map("/subdir", app =>
{
if (!env.IsDevelopment())
app.Map("/reexecution", reexecutionApp =>
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
}

app.UseStaticFiles();
app.UseRouting();
RazorComponentEndpointsStartup<TRootComponent>.UseFakeAuthState(app);
app.UseAntiforgery();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorComponents<TRootComponent>();
reexecutionApp.UseStaticFiles();
reexecutionApp.UseStatusCodePagesWithReExecute("/not-found-reexecute", createScopeForErrors: true);
reexecutionApp.UseRouting();
RazorComponentEndpointsStartup<TRootComponent>.UseFakeAuthState(reexecutionApp);
reexecutionApp.UseAntiforgery();
reexecutionApp.UseEndpoints(endpoints =>
{
endpoints.MapRazorComponents<TRootComponent>();
});
});

ConfigureSubdirPipeline(app, env);
});
}

private void ConfigureSubdirPipeline(IApplicationBuilder app, IWebHostEnvironment env)
{
if (!env.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
}

app.UseStaticFiles();
app.UseRouting();
RazorComponentEndpointsStartup<TRootComponent>.UseFakeAuthState(app);
app.UseAntiforgery();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorComponents<TRootComponent>();
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,20 +76,17 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.Map("/reexecution", reexecutionApp =>
{
reexecutionApp.UseStatusCodePagesWithReExecute("/not-found-reexecute", createScopeForErrors: true);

reexecutionApp.UseRouting();

reexecutionApp.UseAntiforgery();
reexecutionApp.UseEndpoints(endpoints =>
{
endpoints.MapRazorComponents<TRootComponent>();
});
ConfigureEndpoints(reexecutionApp, env);
});

ConfigureSubdirPipeline(app, env);
});
}

protected virtual void ConfigureSubdirPipeline(IApplicationBuilder app, IWebHostEnvironment env)
private void ConfigureSubdirPipeline(IApplicationBuilder app, IWebHostEnvironment env)
{
WebAssemblyTestHelper.ServeCoopHeadersIfWebAssemblyThreadingEnabled(app);

Expand All @@ -106,11 +103,15 @@ protected virtual void ConfigureSubdirPipeline(IApplicationBuilder app, IWebHost
{
if (ctx.Request.Query.ContainsKey("add-csp"))
{
ctx.Response.Headers.Add("Content-Security-Policy", "script-src 'self' 'unsafe-inline'");
ctx.Response.Headers.Add("Content-Security-Policy", "script-src 'self' 'unsafe-inline'");
}
return nxt();
});
ConfigureEndpoints(app, env);
}

private void ConfigureEndpoints(IApplicationBuilder app, IWebHostEnvironment env)
{
_ = app.UseEndpoints(endpoints =>
{
var contentRootStaticAssetsPath = Path.Combine(env.ContentRootPath, "Components.TestServer.staticwebassets.endpoints.json");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@page "/redirection-not-found-ssr-streaming"
@page "/reexecution/redirection-not-found-ssr-streaming"
@attribute [StreamRendering(true)]

<Components.WasmMinimal.Pages.NotFound.RedirectionNotFoundComponent StartStreaming="true" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@page "/redirection-not-found-ssr"
@page "/reexecution/redirection-not-found-ssr"
@attribute [StreamRendering(false)]

<Components.WasmMinimal.Pages.NotFound.RedirectionNotFoundComponent />
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@page "/redirection-not-found"
@page "/reexecution/redirection-not-found"

<RedirectionNotFoundComponent @rendermode="@RenderModeHelper.GetRenderMode(_renderMode)" />

@code{
[Parameter, SupplyParameterFromQuery(Name = "renderMode")]
public string? RenderModeStr { get; set; }

private RenderModeId _renderMode;

protected override void OnInitialized()
{
if (!string.IsNullOrEmpty(RenderModeStr))
{
_renderMode = RenderModeHelper.ParseRenderMode(RenderModeStr);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

@inject NavigationManager NavigationManager

<h1>Original page</h1>

<p id="test-info">Any content</p>
<a id="link-to-not-existing-page" href="@_nonExistingPath">
Go to not-existing-page
</a>

@code{
[Parameter]
[SupplyParameterFromQuery(Name = "navigate-programmatically")]
public bool? NavigateProgrammatically { get; set; }

[Parameter]
public bool StartStreaming { get; set; } = false;

private string _nonExistingPath = string.Empty;

protected override async Task OnInitializedAsync()
{
if (StartStreaming)
{
await Task.Yield();
}
_nonExistingPath = $"{NavigationManager.BaseUri}reexecution/not-existing-page";
if (NavigateProgrammatically == true)
{
NavigationManager.NavigateTo(_nonExistingPath);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;

namespace TestServer;

public static class RenderModeHelper
{
public static IComponentRenderMode GetRenderMode(RenderModeId renderMode)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,12 @@ public static void Main(string[] args)
#endif
}

app.UseStatusCodePagesWithReExecute("/not-found", createScopeForErrors: true);

#if (HasHttpsProfile)
app.UseHttpsRedirection();

#endif
#endif
app.UseAntiforgery();

app.MapStaticAssets();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
app.UseHsts();
#endif
}
app.UseStatusCodePagesWithReExecute("/not-found", createScopeForErrors: true);

#if (HasHttpsProfile)
app.UseHttpsRedirection();
Expand Down
Loading