Skip to content

Commit 19258e5

Browse files
surayya-MSSteveSandersonMSMackinnonBuck
authored
[release/8.0] Fix NavigationManager.Refresh() on SSR-only pages (#52767)
* Fix NavigationManager.Refresh() on SSR-only pages (#52559) * implement NavigationManager.Refresh() on ssr pages --------- Co-authored-by: Steve Sanderson <[email protected]> * update blazor js files * small fix --------- Co-authored-by: Steve Sanderson <[email protected]> Co-authored-by: Mackinnon Buck <[email protected]>
1 parent a4fd5fe commit 19258e5

File tree

9 files changed

+181
-3
lines changed

9 files changed

+181
-3
lines changed

src/Components/Endpoints/src/DependencyInjection/HttpNavigationManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ internal sealed class HttpNavigationManager : NavigationManager, IHostEnvironmen
99
{
1010
void IHostEnvironmentNavigationManager.Initialize(string baseUri, string uri) => Initialize(baseUri, uri);
1111

12-
protected override void NavigateToCore(string uri, bool forceLoad)
12+
protected override void NavigateToCore(string uri, NavigationOptions options)
1313
{
1414
var absoluteUriString = ToAbsoluteUri(uri).ToString();
1515
throw new NavigationException(absoluteUriString);

src/Components/Server/src/Circuits/RemoteNavigationManager.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ async Task PerformNavigationAsync()
120120
/// <inheritdoc />
121121
public override void Refresh(bool forceReload = false)
122122
{
123+
if (_jsRuntime == null)
124+
{
125+
var absoluteUriString = ToAbsoluteUri(Uri).ToString();
126+
throw new NavigationException(absoluteUriString);
127+
}
128+
123129
_ = RefreshAsync();
124130

125131
async Task RefreshAsync()

src/Components/Web.JS/dist/Release/blazor.web.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Components/Web.JS/src/Services/NavigationEnhancement.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,9 @@ export async function performEnhancedPageLoad(internalDestinationHref: string, i
239239
history.replaceState(null, '', response.url);
240240
} else {
241241
// For non-gets, we're still on the source page, so need to append a whole new history entry
242-
history.pushState(null, '', response.url);
242+
if (response.url !== location.href) {
243+
history.pushState(null, '', response.url);
244+
}
243245
}
244246
internalDestinationHref = response.url;
245247
}

src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,6 +1463,26 @@ public void SubmitButtonFormenctypeAttributeOverridesEnhancedFormEnctype()
14631463
Browser.Equal("application/x-www-form-urlencoded", () => Browser.Exists(By.Id("content-type")).Text);
14641464
}
14651465

1466+
[Fact]
1467+
public void EnhancedFormThatCallsNavigationManagerRefreshDoesNotPushHistoryEntry()
1468+
{
1469+
var startUrl = Browser.Url;
1470+
GoTo("forms/form-that-calls-navigation-manager-refresh");
1471+
var guid = Browser.Exists(By.Id("guid")).Text;
1472+
1473+
Browser.Exists(By.Id("submit-button")).Click();
1474+
1475+
// Checking that the page was refreshed.
1476+
// The redirect request method is GET.
1477+
// Providing a Guid to check that it is not the initial GET request for the page
1478+
Browser.NotEqual(guid, () => Browser.Exists(By.Id("guid")).Text);
1479+
Browser.Equal("GET", () => Browser.Exists(By.Id("method")).Text);
1480+
1481+
// Checking that the history entry was not pushed
1482+
Browser.Navigate().Back();
1483+
Browser.Equal(startUrl, () => Browser.Url);
1484+
}
1485+
14661486
// Can't just use GetAttribute or GetDomAttribute because they both auto-resolve it
14671487
// to an absolute URL. We want to be able to assert about the attribute's literal value.
14681488
private string ReadFormActionAttribute(IWebElement form)

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,6 +1143,22 @@ public void CanPerformNavigateToFromInteractiveEventHandler(bool suppressEnhance
11431143
Assert.Equal(shouldPreserveElements, !EnhancedNavigationTestUtil.IsElementStale(originalNavElem));
11441144
}
11451145

1146+
[Fact]
1147+
public void NavigationManagerCanRefreshSSRPageWhenServerInteractivityEnabled()
1148+
{
1149+
Navigate($"{ServerPathBase}/forms/form-that-calls-navigation-manager-refresh");
1150+
1151+
var guid = Browser.Exists(By.Id("guid")).Text;
1152+
1153+
Browser.Exists(By.Id("submit-button")).Click();
1154+
1155+
// Checking that the page was refreshed.
1156+
// The redirect request method is GET.
1157+
// Providing a Guid to check that it is not the initial GET request for the page
1158+
Browser.NotEqual(guid, () => Browser.Exists(By.Id("guid")).Text);
1159+
Browser.Equal("GET", () => Browser.Exists(By.Id("method")).Text);
1160+
}
1161+
11461162
private void BlockWebAssemblyResourceLoad()
11471163
{
11481164
((IJavaScriptExecutor)Browser).ExecuteScript("sessionStorage.setItem('block-load-boot-resource', 'true')");
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Components.TestServer.RazorComponents;
5+
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
6+
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
7+
using Microsoft.AspNetCore.E2ETesting;
8+
using OpenQA.Selenium;
9+
using TestServer;
10+
using Xunit.Abstractions;
11+
12+
namespace Microsoft.AspNetCore.Components.E2ETests.ServerRenderingTests;
13+
14+
[CollectionDefinition(nameof(InteractivityTest), DisableParallelization = true)]
15+
public class NoInteractivityTest : ServerTestBase<BasicTestAppServerSiteFixture<RazorComponentEndpointsNoInteractivityStartup<App>>>
16+
{
17+
public NoInteractivityTest(
18+
BrowserFixture browserFixture,
19+
BasicTestAppServerSiteFixture<RazorComponentEndpointsNoInteractivityStartup<App>> serverFixture,
20+
ITestOutputHelper output)
21+
: base(browserFixture, serverFixture, output)
22+
{
23+
}
24+
25+
public override Task InitializeAsync()
26+
=> InitializeAsync(BrowserFixture.StreamingContext);
27+
28+
[Fact]
29+
public void NavigationManagerCanRefreshSSRPageWhenInteractivityNotPresent()
30+
{
31+
Navigate($"{ServerPathBase}/forms/form-that-calls-navigation-manager-refresh");
32+
33+
var guid = Browser.Exists(By.Id("guid")).Text;
34+
35+
Browser.Exists(By.Id("submit-button")).Click();
36+
37+
// Checking that the page was refreshed.
38+
// The redirect request method is GET.
39+
// Providing a Guid to check that it is not the initial GET request for the page
40+
Browser.NotEqual(guid, () => Browser.Exists(By.Id("guid")).Text);
41+
Browser.Equal("GET", () => Browser.Exists(By.Id("method")).Text);
42+
}
43+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Globalization;
5+
using System.Reflection;
6+
using System.Security.Claims;
7+
using System.Web;
8+
using Components.TestServer.RazorComponents;
9+
using Components.TestServer.RazorComponents.Pages.Forms;
10+
using Components.TestServer.Services;
11+
using Microsoft.AspNetCore.Mvc;
12+
13+
namespace TestServer;
14+
15+
public class RazorComponentEndpointsNoInteractivityStartup<TRootComponent>
16+
{
17+
public RazorComponentEndpointsNoInteractivityStartup(IConfiguration configuration)
18+
{
19+
Configuration = configuration;
20+
}
21+
22+
public IConfiguration Configuration { get; }
23+
24+
// This method gets called by the runtime. Use this method to add services to the container.
25+
public void ConfigureServices(IServiceCollection services)
26+
{
27+
services.AddRazorComponents(options =>
28+
{
29+
options.MaxFormMappingErrorCount = 10;
30+
options.MaxFormMappingRecursionDepth = 5;
31+
options.MaxFormMappingCollectionSize = 100;
32+
});
33+
services.AddHttpContextAccessor();
34+
}
35+
36+
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
37+
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
38+
{
39+
var enUs = new CultureInfo("en-US");
40+
CultureInfo.DefaultThreadCurrentCulture = enUs;
41+
CultureInfo.DefaultThreadCurrentUICulture = enUs;
42+
43+
if (env.IsDevelopment())
44+
{
45+
app.UseDeveloperExceptionPage();
46+
}
47+
48+
app.Map("/subdir", app =>
49+
{
50+
if (!env.IsDevelopment())
51+
{
52+
app.UseExceptionHandler("/Error", createScopeForErrors: true);
53+
}
54+
55+
app.UseStaticFiles();
56+
app.UseRouting();
57+
app.UseAntiforgery();
58+
app.UseEndpoints(endpoints =>
59+
{
60+
endpoints.MapRazorComponents<TRootComponent>();
61+
});
62+
});
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
@page "/forms/form-that-calls-navigation-manager-refresh"
2+
@using Microsoft.AspNetCore.Components.Forms
3+
@inject NavigationManager Nav
4+
@inject IHttpContextAccessor HttpContextAccessor
5+
6+
<h3>Form That Calls NavigationManager.Refresh()</h3>
7+
8+
<form data-enhance @onsubmit="@(() => Nav.Refresh())" @formname="form-refresh" method="post">
9+
<AntiforgeryToken />
10+
<button id="submit-button">Submit</button>
11+
</form>
12+
13+
<p>Method: <span id="method">@_method</span></p>
14+
15+
<p>Guid: <span id="guid">@Guid</span></p>
16+
17+
@code {
18+
private string? _method = "";
19+
20+
private Guid Guid = Guid.NewGuid();
21+
22+
protected override void OnInitialized()
23+
{
24+
var httpContext = HttpContextAccessor.HttpContext;
25+
_method = httpContext?.Request.Method;
26+
}
27+
}

0 commit comments

Comments
 (0)