Skip to content

Commit dba3968

Browse files
dotnet-maestro-botMackinnonBuckhalter73BrennanConroywtgodbe
authored
[automated] Merge branch 'release/8.0' => 'main' (#50973)
* [release/8.0-rc2] [Blazor] Make auto components prefer the existing render mode (#50851) # Make auto components prefer the existing render mode Ensures that if interactive components exist on the page and they all use a single render mode, components with the "Auto" render mode will also use that render mode. ## Description It's a common requirement for state to be shared between interactive root components. For example, the `<HeadOutlet>` component, which allows updating content in the HTML `<head>`, renders content specified by a `<HeadContent>` component, if it exists. However, this only works if the `<HeadOutlet>` and `<HeadContent>` components use the same interactive render mode. The Auto render mode lets Blazor select a "best" render mode (either Server or WebAssembly) at runtime, primarily depending on whether Blazor WebAssembly resources are cached in the browser. However, previously it did this without taking into consideration the render mode used by existing components on the page. For example, suppose a `<HeadOutlet>` with an Auto render mode gets added to the page before WebAssembly resources have been loaded and cached, so it uses Blazor Server. Later, a `<HeadContent>` component, also with an Auto render mode, gets added to the page. But at this point, WebAssembly resources have loaded completely, so the `<HeadContent>` component uses Blazor WebAssembly. In this example, the content in the `<head>` does not update, because the `<HeadOutlet>` and `<HeadContent>` use different render modes. This PR updates the auto render mode decision making logic to the following: * If WebAssembly components exist on the page, use WebAssembly * Otherwise, if Server components exist on the page, use Server * Otherwise, if WebAssembly resources are cached, use WebAssembly * Otherwise, use Server Fixes #50686 ## Customer Impact Medium/high. We've seen multiple reports of the existing behavior causing problems in customer apps, and there isn't a workaround. ## Regression? - [ ] Yes - [X] No This behavior has existed since the Auto render mode was introduced in an earlier .NET 8 preview release. ## Risk - [ ] High - [ ] Medium - [X] Low This PR is a small change to the Auto mode logic. It's unlikely that customers were already relying on the existing behavior. ## Verification - [X] Manual (required) - [X] Automated ## Packaging changes reviewed? - [ ] Yes - [ ] No - [X] N/A * Add Identity Components to Blazor template (#50722) # Add Identity Components to Blazor template ## Description This adds the option to add Identity Razor Components (`*.razor` files) when using the Blazor project template. This supports the same feature set as the Identity Razor Pages (`*.cshtml` files). We've already done an accessibility pass over these newly added components and this PR includes all the fixes for Accessibility too. As with the Identity Razor Pages, this supports local authentication (with the option to configure external login providers) and Identity management using EF Core. Fixes #48786 ## Customer Impact These Identity Razor Components have been a common request for years as noted above, because it allows Blazor developers to use Identity without needing to add Razor Pages infrastructure which would otherwise be unnecessary and doesn't integrate well with the rest of the app. For example, the Identity Razor Pages to a Blazor app would use a different layout that doesn't match the look and feel of the Razor Components that make up the rest of the app. ## Regression? - [ ] Yes - [x] No ## Risk - [ ] High - [ ] Medium - [x] Low These are template only changes that only affect the brand new Blazor project template. It should have no impact on the project template output unless you opt-in to the individual auth option (other than a [small fix](5462e42) to make the `--empty` option produce compileable output with all `InteractivityPlatform` options.) ## Verification - [x] Manual (required) - [x] Automated We're also adding new validation scenarios for vendors to validate periodically. ## Packaging changes reviewed? - [ ] Yes - [ ] No - [x] N/A ---- - [x] Finish user management components for 2fa, external login, personal data, etc... - [x] Add signout link - [x] Verify RegisterOnPersisting gets invoked as expected with the changes from #50625 - [x] Render ShowRecoveryCodes.razor inline rather than via a redirect. - [x] Add baseline tests - [x] Fix BOMs - [x] Validate the template in VS * Add logging to Http.Sys tests (#50883) * Skip failing tests (#50917) * Avoid re-running routing for implicit middlewares and remove implicit anti-forgery (#50864) ## Description Avoid running routing eagerly in implicit middlewares to prevents the `EndpointFeature` from being set and causing unexpected reactions in other middlewares, like the static file middleware. We also remove the implicit registration of the anti-forgery middleware to avoid unintended collisions with authentication in Blazor. Fixes #50818, #50815, #50844 ## Customer Impact Without this change, the anti-forgery middleware in Blazor apps runs too early and is not able to examine authentication state in the application. Requiring the middleware to be registered explicitly ensures that the correct ordering is applied. Without this change, users will run into difficult to resolve issues with building applications that include forms with Blazor web apps. ## Regression? - [X] Yes - [ ] No This is a regression that was introduced to middleware routing in .NET 8 Preview 7. ## Risk - [ ] High - [X] Medium - [ ] Low **Medium risk** because: - We are reverting a change that was originally applied to resolve #49654. This means that the original bug will impact users, specifically those who are calling `UseRouting` explicitly without calling `UseAuthentication` and `UseAuthorization` if they are not available. There is a workaround that we plan to document this behavior for users. - Apps deployed in .NET 8 RC 1 will break because we no longer automatically enable the anti-forgery middleware. Users will receive an exception at startup notifying them of the code changes to make in order to get things working correctly. ## Verification - [X] Manual (required) - [X] Automated ## Packaging changes reviewed? - [ ] Yes - [ ] No - [X] N/A * [release/8.0] Update dependencies from dotnet/runtime (#50957) * Update dependencies from https://github.com/dotnet/runtime build 20230926.15 Microsoft.Bcl.AsyncInterfaces , Microsoft.Bcl.TimeProvider , Microsoft.Extensions.Caching.Abstractions , Microsoft.Extensions.Caching.Memory , Microsoft.Extensions.Configuration , Microsoft.Extensions.Configuration.Abstractions , Microsoft.Extensions.Configuration.Binder , Microsoft.Extensions.Configuration.CommandLine , Microsoft.Extensions.Configuration.EnvironmentVariables , Microsoft.Extensions.Configuration.FileExtensions , Microsoft.Extensions.Configuration.Ini , Microsoft.Extensions.Configuration.Json , Microsoft.Extensions.Configuration.UserSecrets , Microsoft.Extensions.Configuration.Xml , Microsoft.Extensions.DependencyInjection , Microsoft.Extensions.DependencyInjection.Abstractions , Microsoft.Extensions.DependencyModel , Microsoft.Extensions.Diagnostics , Microsoft.Extensions.Diagnostics.Abstractions , Microsoft.Extensions.FileProviders.Abstractions , Microsoft.Extensions.FileProviders.Composite , Microsoft.Extensions.FileProviders.Physical , Microsoft.Extensions.FileSystemGlobbing , Microsoft.Extensions.HostFactoryResolver.Sources , Microsoft.Extensions.Hosting , Microsoft.Extensions.Hosting.Abstractions , Microsoft.Extensions.Http , Microsoft.Extensions.Logging , Microsoft.Extensions.Logging.Abstractions , Microsoft.Extensions.Logging.Configuration , Microsoft.Extensions.Logging.Console , Microsoft.Extensions.Logging.Debug , Microsoft.Extensions.Logging.EventLog , Microsoft.Extensions.Logging.EventSource , Microsoft.Extensions.Logging.TraceSource , Microsoft.Extensions.Options , Microsoft.Extensions.Options.ConfigurationExtensions , Microsoft.Extensions.Options.DataAnnotations , Microsoft.Extensions.Primitives , Microsoft.Internal.Runtime.AspNetCore.Transport , Microsoft.NET.Runtime.MonoAOTCompiler.Task , Microsoft.NET.Runtime.WebAssembly.Sdk , Microsoft.NETCore.App.Ref , Microsoft.NETCore.App.Runtime.AOT.win-x64.Cross.browser-wasm , Microsoft.NETCore.App.Runtime.win-x64 , Microsoft.NETCore.BrowserDebugHost.Transport , Microsoft.NETCore.Platforms , System.Collections.Immutable , System.Composition , System.Configuration.ConfigurationManager , System.Diagnostics.DiagnosticSource , System.Diagnostics.EventLog , System.Diagnostics.PerformanceCounter , System.DirectoryServices.Protocols , System.IO.Hashing , System.IO.Pipelines , System.Net.Http.Json , System.Net.Http.WinHttpHandler , System.Reflection.Metadata , System.Resources.Extensions , System.Runtime.Caching , System.Security.Cryptography.Pkcs , System.Security.Cryptography.Xml , System.Security.Permissions , System.ServiceProcess.ServiceController , System.Text.Encodings.Web , System.Text.Json , System.Threading.AccessControl , System.Threading.Channels , System.Threading.RateLimiting , Microsoft.SourceBuild.Intermediate.runtime.linux-x64 From Version 8.0.0-rtm.23475.7 -> To Version 8.0.0-rtm.23476.15 * Update node version --------- Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com> Co-authored-by: jacalvar <jacalvar@microsoft.com> * [Blazor] Antiforgery fix (#50946) # Ensure antiforgery token flows to Blazor WebAssembly The change makes sure that we persist the Antiforgery token during prerendering so that it is available to WebAssembly components. ## Description * When using cookie authentication it is necessary to use antiforgery protection to prevent cross-site request forgery attacks. * Blazor Webassembly interactive components must get access to the request antiforgery token to attach it to any outgoing API call. * The antiforgery request token was not flowing from the server to the client correctly. * This change enables calling APIs from web assembly to the server safely. Fixes #50900 ## Customer Impact .NET 8.0 customers who have created Blazor Web Apps will fail to call APIs from webassembly components, as they won't be able to attach the required antiforgery token. ## Regression? - [ ] Yes - [X] No [If yes, specify the version the behavior has regressed from] ## Risk - [ ] High - [ ] Medium - [X] Low The fix is simple and we added an E2E test to cover the scenario. ## Verification - [ ] Manual (required) - [X] Automated ## Packaging changes reviewed? - [ ] Yes - [ ] No - [X] N/A ---- ## When servicing release/2.1 - [ ] Make necessary changes in eng/PatchConfig.props * Update dependencies from https://github.com/dotnet/runtime build 20230927.9 (#50977) Microsoft.Bcl.AsyncInterfaces , Microsoft.Bcl.TimeProvider , Microsoft.Extensions.Caching.Abstractions , Microsoft.Extensions.Caching.Memory , Microsoft.Extensions.Configuration , Microsoft.Extensions.Configuration.Abstractions , Microsoft.Extensions.Configuration.Binder , Microsoft.Extensions.Configuration.CommandLine , Microsoft.Extensions.Configuration.EnvironmentVariables , Microsoft.Extensions.Configuration.FileExtensions , Microsoft.Extensions.Configuration.Ini , Microsoft.Extensions.Configuration.Json , Microsoft.Extensions.Configuration.UserSecrets , Microsoft.Extensions.Configuration.Xml , Microsoft.Extensions.DependencyInjection , Microsoft.Extensions.DependencyInjection.Abstractions , Microsoft.Extensions.DependencyModel , Microsoft.Extensions.Diagnostics , Microsoft.Extensions.Diagnostics.Abstractions , Microsoft.Extensions.FileProviders.Abstractions , Microsoft.Extensions.FileProviders.Composite , Microsoft.Extensions.FileProviders.Physical , Microsoft.Extensions.FileSystemGlobbing , Microsoft.Extensions.HostFactoryResolver.Sources , Microsoft.Extensions.Hosting , Microsoft.Extensions.Hosting.Abstractions , Microsoft.Extensions.Http , Microsoft.Extensions.Logging , Microsoft.Extensions.Logging.Abstractions , Microsoft.Extensions.Logging.Configuration , Microsoft.Extensions.Logging.Console , Microsoft.Extensions.Logging.Debug , Microsoft.Extensions.Logging.EventLog , Microsoft.Extensions.Logging.EventSource , Microsoft.Extensions.Logging.TraceSource , Microsoft.Extensions.Options , Microsoft.Extensions.Options.ConfigurationExtensions , Microsoft.Extensions.Options.DataAnnotations , Microsoft.Extensions.Primitives , Microsoft.Internal.Runtime.AspNetCore.Transport , Microsoft.NET.Runtime.MonoAOTCompiler.Task , Microsoft.NET.Runtime.WebAssembly.Sdk , Microsoft.NETCore.App.Ref , Microsoft.NETCore.App.Runtime.AOT.win-x64.Cross.browser-wasm , Microsoft.NETCore.App.Runtime.win-x64 , Microsoft.NETCore.BrowserDebugHost.Transport , Microsoft.NETCore.Platforms , System.Collections.Immutable , System.Composition , System.Configuration.ConfigurationManager , System.Diagnostics.DiagnosticSource , System.Diagnostics.EventLog , System.Diagnostics.PerformanceCounter , System.DirectoryServices.Protocols , System.IO.Hashing , System.IO.Pipelines , System.Net.Http.Json , System.Net.Http.WinHttpHandler , System.Reflection.Metadata , System.Resources.Extensions , System.Runtime.Caching , System.Security.Cryptography.Pkcs , System.Security.Cryptography.Xml , System.Security.Permissions , System.ServiceProcess.ServiceController , System.Text.Encodings.Web , System.Text.Json , System.Threading.AccessControl , System.Threading.Channels , System.Threading.RateLimiting , Microsoft.SourceBuild.Intermediate.runtime.linux-x64 From Version 8.0.0-rtm.23476.15 -> To Version 8.0.0-rtm.23477.9 Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com> --------- Co-authored-by: Mackinnon Buck <mackinnon.buck@gmail.com> Co-authored-by: Stephen Halter <halter73@gmail.com> Co-authored-by: Brennan <brecon@microsoft.com> Co-authored-by: William Godbe <wigodbe@microsoft.com> Co-authored-by: Safia Abdalla <safia@microsoft.com> Co-authored-by: dotnet-maestro[bot] <42748379+dotnet-maestro[bot]@users.noreply.github.com> Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com> Co-authored-by: jacalvar <jacalvar@microsoft.com>
1 parent 6c5eec8 commit dba3968

File tree

129 files changed

+6006
-790
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

129 files changed

+6006
-790
lines changed

eng/targets/Helix.props

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<HelixTimeout>00:45:00</HelixTimeout>
1313
<HelixTestName>$(MSBuildProjectName)--$(TargetFramework)</HelixTestName>
1414
<LoggingTestingDisableFileLogging Condition="'$(IsHelixJob)' == 'true'">false</LoggingTestingDisableFileLogging>
15-
<NodeVersion>16.11.0</NodeVersion>
15+
<NodeVersion>20.7.0</NodeVersion>
1616

1717
<!--
1818
Many tests depend on the .dotnet/ layouts but only a few (those that restore or test packages) require

src/Antiforgery/src/AntiforgeryApplicationBuilderExtensions.cs

-15
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
using Microsoft.AspNetCore.Antiforgery;
55
using Microsoft.AspNetCore.Antiforgery.Internal;
6-
using Microsoft.AspNetCore.Routing;
7-
using Microsoft.Extensions.DependencyInjection;
86

97
namespace Microsoft.AspNetCore.Builder;
108

@@ -26,19 +24,6 @@ public static IApplicationBuilder UseAntiforgery(this IApplicationBuilder builde
2624
builder.VerifyAntiforgeryServicesAreRegistered();
2725

2826
builder.Properties[AntiforgeryMiddlewareSetKey] = true;
29-
30-
// The anti-forgery middleware adds annotations to HttpContext.Items to indicate that it has run
31-
// that will be validated by the EndpointsRoutingMiddleware later. To do this, we need to ensure
32-
// that routing has run and set the endpoint feature on the HttpContext associated with the request.
33-
if (builder.Properties.TryGetValue(RerouteHelper.GlobalRouteBuilderKey, out var routeBuilder) && routeBuilder is not null)
34-
{
35-
return builder.Use(next =>
36-
{
37-
var newNext = RerouteHelper.Reroute(builder, routeBuilder, next);
38-
var antiforgery = builder.ApplicationServices.GetRequiredService<IAntiforgery>();
39-
return new AntiforgeryMiddleware(antiforgery, newNext).Invoke;
40-
});
41-
}
4227
builder.UseMiddleware<AntiforgeryMiddleware>();
4328

4429
return builder;

src/Antiforgery/src/Microsoft.AspNetCore.Antiforgery.csproj

-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,5 @@
2626

2727
<ItemGroup>
2828
<Compile Include="$(SharedSourceRoot)HttpExtensions.cs" LinkBase="Shared"/>
29-
<Compile Include="$(SharedSourceRoot)Reroute.cs" LinkBase="Shared"/>
3029
</ItemGroup>
3130
</Project>

src/Components/Shared/src/DefaultAntiforgeryStateProvider.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ public DefaultAntiforgeryStateProvider(PersistentComponentState state)
2424
// don't have access to the request.
2525
_subscription = state.RegisterOnPersisting(() =>
2626
{
27-
state.PersistAsJson(PersistenceKey, _currentToken);
27+
state.PersistAsJson(PersistenceKey, GetAntiforgeryToken());
2828
return Task.CompletedTask;
29-
}, new InteractiveAutoRenderMode());
29+
}, RenderMode.InteractiveWebAssembly);
3030

3131
state.TryTakeFromJson(PersistenceKey, out _currentToken);
3232
}

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

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

+35-17
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type RootComponentInfo = {
3737
assignedRendererId?: WebRendererId;
3838
uniqueIdAtLastUpdate?: number;
3939
interactiveComponentId?: number;
40-
}
40+
};
4141

4242
export class WebRootComponentManager implements DescriptorHandler, RootComponentManager<never> {
4343
private readonly _rootComponents = new Set<RootComponentInfo>();
@@ -193,7 +193,7 @@ export class WebRootComponentManager implements DescriptorHandler, RootComponent
193193
}
194194

195195
private circuitMayHaveNoRootComponents() {
196-
const isCircuitInUse = this.hasAnyExistingOrPendingServerComponents();
196+
const isCircuitInUse = this.rendererHasExistingOrPendingComponents(WebRendererId.Server, 'server', 'auto');
197197
if (isCircuitInUse) {
198198
// Clear the timeout because we know the circuit is in use.
199199
clearTimeout(this._circuitInactivityTimeoutId);
@@ -208,31 +208,38 @@ export class WebRootComponentManager implements DescriptorHandler, RootComponent
208208

209209
// Start a new timeout to dispose the circuit unless it starts getting used.
210210
this._circuitInactivityTimeoutId = setTimeout(() => {
211-
if (!this.hasAnyExistingOrPendingServerComponents()) {
211+
if (!this.rendererHasExistingOrPendingComponents(WebRendererId.Server, 'server', 'auto')) {
212212
disposeCircuit();
213213
this._circuitInactivityTimeoutId = undefined;
214214
}
215215
}, this._circuitInactivityTimeoutMs) as unknown as number;
216216
}
217217

218-
private hasAnyExistingOrPendingServerComponents(): boolean {
219-
// If there are active Blazor Server components on the page, we shouldn't dispose the circuit.
220-
const renderer = getRendererer(WebRendererId.Server);
221-
if (renderer && renderer.getRootComponentCount() > 0) {
218+
private rendererHasComponents(rendererId: WebRendererId): boolean {
219+
const renderer = getRendererer(rendererId);
220+
return renderer !== undefined && renderer.getRootComponentCount() > 0;
221+
}
222+
223+
private rendererHasExistingOrPendingComponents(rendererId: WebRendererId, ...descriptorTypesToConsider: ComponentMarker['type'][]): boolean {
224+
if (this.rendererHasComponents(rendererId)) {
222225
return true;
223226
}
224227

225-
// If we have SSR components that may become Blazor Server components in the future,
226-
// we shouldn't dispose the circuit.
228+
// We consider SSR'd components on the page that may get activated using the specified renderer.
227229
for (const { descriptor: { type }, assignedRendererId } of this._rootComponents) {
228-
if (assignedRendererId === WebRendererId.Server) {
229-
// The component has been assigned to use Blazor Server.
230+
if (assignedRendererId === rendererId) {
231+
// The component has been assigned to use the specified renderer.
230232
return true;
231233
}
232234

233-
if (assignedRendererId === undefined && (type === 'auto' || type === 'server')) {
234-
// The component has not been assigned a renderer yet, so it's possible it might
235-
// use Blazor Server.
235+
if (assignedRendererId !== undefined) {
236+
// The component has been assigned to use another renderer.
237+
continue;
238+
}
239+
240+
if (descriptorTypesToConsider.indexOf(type) !== -1) {
241+
// The component has not been assigned a renderer yet, but it might get activated with the specified renderer
242+
// if it doesn't get removed from the page.
236243
return true;
237244
}
238245
}
@@ -298,9 +305,20 @@ export class WebRootComponentManager implements DescriptorHandler, RootComponent
298305
}
299306

300307
private getAutoRenderMode(): 'webassembly' | 'server' | null {
301-
// If the WebAssembly runtime has loaded, we will always use WebAssembly
302-
// for auto components. Otherwise, we'll wait to activate root components
303-
// until we determine whether the WebAssembly runtime can be loaded quickly.
308+
// If WebAssembly components exist or may exist soon, use WebAssembly.
309+
if (this.rendererHasExistingOrPendingComponents(WebRendererId.WebAssembly, 'webassembly')) {
310+
return 'webassembly';
311+
}
312+
313+
// If Server components exist or may exist soon, use WebAssembly.
314+
if (this.rendererHasExistingOrPendingComponents(WebRendererId.Server, 'server')) {
315+
return 'server';
316+
}
317+
318+
// If no interactive components are on the page, we use WebAssembly
319+
// if the WebAssembly runtime has loaded. Otherwise, we'll wait to activate
320+
// root components until we determine whether the WebAssembly runtime can be
321+
// loaded quickly.
304322
if (hasLoadedWebAssemblyPlatform()) {
305323
return 'webassembly';
306324
}

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

+13
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,19 @@ public void FormNoAntiforgeryReturnBadRequest(bool suppressEnhancedNavigation)
896896
DispatchToFormCore(dispatchToForm);
897897
}
898898

899+
[Fact]
900+
public void CanUseAntiforgeryTokenInWasm()
901+
{
902+
var dispatchToForm = new DispatchToForm(this)
903+
{
904+
Url = "forms/antiforgery-wasm",
905+
FormCssSelector = "form",
906+
InputFieldId = "Value",
907+
SuppressEnhancedNavigation = true,
908+
};
909+
DispatchToFormCore(dispatchToForm);
910+
}
911+
899912
[Theory]
900913
[InlineData(true)]
901914
[InlineData(false)]

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

+78
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,84 @@ public void AutoRenderMode_CanUseBlazorWebAssembly_WhenMultipleAutoComponentsAre
720720
Browser.Equal("WebAssembly", () => Browser.FindElement(By.Id("render-mode-2")).Text);
721721
}
722722

723+
[Fact]
724+
public void AutoRenderMode_UsesBlazorWebAssembly_WhenBothServerAndWebAssemblyComponentsExist()
725+
{
726+
Navigate($"{ServerPathBase}/streaming-interactivity");
727+
Browser.Equal("Not streaming", () => Browser.FindElement(By.Id("status")).Text);
728+
729+
Browser.Click(By.Id(AddWebAssemblyPrerenderedId));
730+
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-0")).Text);
731+
Browser.Equal("WebAssembly", () => Browser.FindElement(By.Id("render-mode-0")).Text);
732+
733+
Browser.Click(By.Id(AddServerPrerenderedId));
734+
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-1")).Text);
735+
Browser.Equal("Server", () => Browser.FindElement(By.Id("render-mode-1")).Text);
736+
737+
Browser.Click(By.Id(AddAutoPrerenderedId));
738+
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-2")).Text);
739+
Browser.Equal("WebAssembly", () => Browser.FindElement(By.Id("render-mode-2")).Text);
740+
}
741+
742+
[Fact]
743+
public void AutoRenderMode_UsesBlazorServer_WhenOnlyServerComponentsExist_EvenAfterWebAssemblyResourcesLoad()
744+
{
745+
Navigate(ServerPathBase);
746+
Browser.Equal("Hello", () => Browser.Exists(By.TagName("h1")).Text);
747+
ForceWebAssemblyResourceCacheMiss();
748+
749+
Navigate($"{ServerPathBase}/streaming-interactivity");
750+
Browser.Equal("Not streaming", () => Browser.FindElement(By.Id("status")).Text);
751+
752+
// We start by adding a WebAssembly component to ensure the WebAssembly runtime
753+
// will be cached after we refresh the page.
754+
Browser.Click(By.Id(AddWebAssemblyPrerenderedId));
755+
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-0")).Text);
756+
Browser.Equal("WebAssembly", () => Browser.FindElement(By.Id("render-mode-0")).Text);
757+
758+
Browser.Click(By.Id($"remove-counter-link-0"));
759+
Browser.DoesNotExist(By.Id("is-interactive-0"));
760+
761+
Browser.Navigate().Refresh();
762+
763+
Browser.Click(By.Id(AddServerPrerenderedId));
764+
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-1")).Text);
765+
Browser.Equal("Server", () => Browser.FindElement(By.Id("render-mode-1")).Text);
766+
767+
// Verify that Auto mode will use Blazor Server, even though the WebAssembly runtime is cached
768+
Browser.Click(By.Id(AddAutoPrerenderedId));
769+
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-2")).Text);
770+
Browser.Equal("Server", () => Browser.FindElement(By.Id("render-mode-2")).Text);
771+
}
772+
773+
[Fact]
774+
public void AutoRenderMode_UsesBlazorServer_AfterWebAssemblyComponentsNoLongerExist_ButServerComponentsDo()
775+
{
776+
Navigate($"{ServerPathBase}/streaming-interactivity");
777+
Browser.Equal("Not streaming", () => Browser.FindElement(By.Id("status")).Text);
778+
779+
Browser.Click(By.Id(AddWebAssemblyPrerenderedId));
780+
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-0")).Text);
781+
Browser.Equal("WebAssembly", () => Browser.FindElement(By.Id("render-mode-0")).Text);
782+
783+
Browser.Click(By.Id(AddServerPrerenderedId));
784+
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-1")).Text);
785+
Browser.Equal("Server", () => Browser.FindElement(By.Id("render-mode-1")).Text);
786+
787+
Browser.Click(By.Id(AddAutoPrerenderedId));
788+
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-2")).Text);
789+
Browser.Equal("WebAssembly", () => Browser.FindElement(By.Id("render-mode-2")).Text);
790+
791+
// Remove all WebAssembly components
792+
Browser.Click(By.Id("remove-counter-link-0"));
793+
Browser.Click(By.Id("remove-counter-link-2"));
794+
795+
// Verify that Blazor Server gets used
796+
Browser.Click(By.Id(AddAutoPrerenderedId));
797+
Browser.Equal("True", () => Browser.FindElement(By.Id("is-interactive-3")).Text);
798+
Browser.Equal("Server", () => Browser.FindElement(By.Id("render-mode-3")).Text);
799+
}
800+
723801
[Fact]
724802
public void Circuit_ShutsDown_WhenAllBlazorServerComponentsGetRemoved()
725803
{

src/Components/test/testassets/Components.TestServer/Components.TestServer.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<Reference Include="Microsoft.AspNetCore.Authentication.Cookies" />
1515
<Reference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" />
1616
<Reference Include="Microsoft.AspNetCore.Components.Server" />
17+
<Reference Include="Microsoft.AspNetCore.Http.Results" />
1718
<Reference Include="Microsoft.AspNetCore.Cors" />
1819
<Reference Include="Microsoft.AspNetCore.Mvc" />
1920
<Reference Include="Microsoft.AspNetCore.Components.Server" />

src/Components/test/testassets/Components.TestServer/RazorComponentEndpointsStartup.cs

+6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Components.TestServer.RazorComponents.Pages.Forms;
1010
using Components.TestServer.Services;
1111
using Microsoft.AspNetCore.Components.WebAssembly.Server;
12+
using Microsoft.AspNetCore.Mvc;
1213

1314
namespace TestServer;
1415

@@ -154,6 +155,11 @@ private static void MapEnhancedNavigationEndpoints(IEndpointRouteBuilder endpoin
154155
await response.WriteAsync("<html><body><h1>This is a non-Blazor endpoint</h1><p>That's all</p></body></html>");
155156
});
156157

158+
endpoints.MapPost("api/antiforgery-form", ([FromForm] string value) =>
159+
{
160+
return Results.Ok(value);
161+
});
162+
157163
static Task PerformRedirection(HttpRequest request, HttpResponse response)
158164
{
159165
response.Redirect(request.Query["external"] == "true"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@page "/forms/antiforgery-wasm"
2+
<h3>FormRunningOnWasmCanUseAntiforgeryToken</h3>
3+
4+
<TestContentPackage.WasmFormComponent/>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
@attribute [RenderModeInteractiveWebAssembly]
2+
@using Microsoft.AspNetCore.Components.Forms
3+
@using System.Net.Http
4+
<h3>WasmFormComponent</h3>
5+
6+
<form @formname="WasmForm" @onsubmit="SubmitForm">
7+
<input id="Value" @bind="_value" name="Value" />
8+
@if (OperatingSystem.IsBrowser())
9+
{
10+
<input id="send" type="submit" value="Send" />
11+
}
12+
<AntiforgeryToken />
13+
</form>
14+
15+
@if (_posted)
16+
{
17+
@if (_succeeded)
18+
{
19+
<p id="pass">Posting the value succeded.</p>
20+
}
21+
else
22+
{
23+
<p>Posting the value failed.</p>
24+
}
25+
}
26+
else
27+
{
28+
<p>Antiforgery: @_token</p>
29+
}
30+
31+
@code {
32+
string _value;
33+
string _token;
34+
bool _succeeded;
35+
bool _posted;
36+
37+
[Inject] public AntiforgeryStateProvider AntiforgeryState { get; set; }
38+
[Inject] public NavigationManager Navigation { get; set; }
39+
40+
protected override void OnInitialized()
41+
{
42+
if (OperatingSystem.IsBrowser())
43+
{
44+
var antiforgery = AntiforgeryState.GetAntiforgeryToken();
45+
_token = antiforgery.Value;
46+
}
47+
}
48+
49+
private async Task SubmitForm()
50+
{
51+
if (OperatingSystem.IsBrowser())
52+
{
53+
var client = new HttpClient() { BaseAddress = new Uri(Navigation.BaseUri) };
54+
var antiforgery = AntiforgeryState.GetAntiforgeryToken();
55+
if (antiforgery != null)
56+
{
57+
_posted = true;
58+
var request = new HttpRequestMessage(HttpMethod.Post, "api/antiforgery-form");
59+
var content = new FormUrlEncodedContent(new KeyValuePair<string, string>[] { new ("Value", _value) });
60+
request.Content = content;
61+
request.Headers.Add("RequestVerificationToken", antiforgery.Value);
62+
var response = await client.SendAsync(request);
63+
if (response.IsSuccessStatusCode)
64+
{
65+
_succeeded = true;
66+
}
67+
else
68+
{
69+
_succeeded = false;
70+
}
71+
}
72+
}
73+
}
74+
}

src/DefaultBuilder/src/WebApplicationBuilder.cs

-11
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System.Diagnostics;
55
using System.Diagnostics.CodeAnalysis;
66
using System.Reflection;
7-
using Microsoft.AspNetCore.Antiforgery;
87
using Microsoft.AspNetCore.Authentication;
98
using Microsoft.AspNetCore.Authorization;
109
using Microsoft.AspNetCore.Hosting;
@@ -25,7 +24,6 @@ public sealed class WebApplicationBuilder : IHostApplicationBuilder
2524
private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder";
2625
private const string AuthenticationMiddlewareSetKey = "__AuthenticationMiddlewareSet";
2726
private const string AuthorizationMiddlewareSetKey = "__AuthorizationMiddlewareSet";
28-
private const string AntiforgeryMiddlewareSetKey = "__AntiforgeryMiddlewareSet";
2927
private const string UseRoutingKey = "__UseRouting";
3028

3129
private readonly HostApplicationBuilder _hostApplicationBuilder;
@@ -453,15 +451,6 @@ private void ConfigureApplication(WebHostBuilderContext context, IApplicationBui
453451
}
454452
}
455453

456-
if (serviceProviderIsService?.IsService(typeof(IAntiforgery)) is true)
457-
{
458-
if (!_builtApplication.Properties.ContainsKey(AntiforgeryMiddlewareSetKey))
459-
{
460-
_builtApplication.Properties[AntiforgeryMiddlewareSetKey] = true;
461-
app.UseAntiforgery();
462-
}
463-
}
464-
465454
// Wire the source pipeline to run in the destination pipeline
466455
var wireSourcePipeline = new WireSourcePipeline(_builtApplication);
467456
app.Use(wireSourcePipeline.CreateMiddleware);

0 commit comments

Comments
 (0)