From 489cab4e63c9676fdfcda5c56be184c3dec34630 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Thu, 4 Apr 2024 12:47:03 -0400 Subject: [PATCH 1/8] Shorten class name (#32258) --- aspnetcore/blazor/security/server/index.md | 37 ++++++++++------------ 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/aspnetcore/blazor/security/server/index.md b/aspnetcore/blazor/security/server/index.md index 877725bcdb1d..877698ba47b8 100644 --- a/aspnetcore/blazor/security/server/index.md +++ b/aspnetcore/blazor/security/server/index.md @@ -231,14 +231,14 @@ If the app requires a custom provider, implement GetAuthenticationStateAsync() { @@ -256,22 +256,21 @@ public class CustomAuthenticationStateProvider : AuthenticationStateProvider :::moniker range=">= aspnetcore-8.0" -The `CustomAuthenticationStateProvider` service is registered in the `Program` file: +The `CustomAuthStateProvider` service is registered in the `Program` file: ```csharp using Microsoft.AspNetCore.Components.Authorization; ... -builder.Services.AddScoped(); +builder.Services.AddScoped(); ``` :::moniker-end :::moniker range=">= aspnetcore-6.0 < aspnetcore-8.0" -The `CustomAuthenticationStateProvider` service is registered in the `Program` file ***after*** the call to : +The `CustomAuthStateProvider` service is registered in the `Program` file ***after*** the call to : ```csharp using Microsoft.AspNetCore.Components.Authorization; @@ -282,15 +281,14 @@ builder.Services.AddServerSideBlazor(); ... -builder.Services.AddScoped(); +builder.Services.AddScoped(); ``` :::moniker-end :::moniker range="< aspnetcore-6.0" -The `CustomAuthenticationStateProvider` service is registered in `Startup.ConfigureServices` of `Startup.cs` ***after*** the call to : +The `CustomAuthStateProvider` service is registered in `Startup.ConfigureServices` of `Startup.cs` ***after*** the call to : ```csharp using Microsoft.AspNetCore.Components.Authorization; @@ -301,8 +299,7 @@ services.AddServerSideBlazor(); ... -services.AddScoped(); +services.AddScoped(); ``` :::moniker-end @@ -376,16 +373,16 @@ A [custom `AuthenticationStateProvider`](#implement-a-custom-authenticationstate The following example is based on implementing a custom by following the guidance in the [Implement a custom `AuthenticationStateProvider`](#implement-a-custom-authenticationstateprovider) section. -The following `CustomAuthenticationStateProvider` implementation exposes a custom method, `AuthenticateUser`, to sign in a user and notify consumers of the authentication state change. +The following `CustomAuthStateProvider` implementation exposes a custom method, `AuthenticateUser`, to sign in a user and notify consumers of the authentication state change. -`CustomAuthenticationStateProvider.cs`: +`CustomAuthStateProvider.cs`: ```csharp using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Components.Authorization; -public class CustomAuthenticationStateProvider : AuthenticationStateProvider +public class CustomAuthStateProvider : AuthenticationStateProvider { public override Task GetAuthenticationStateAsync() { @@ -414,7 +411,7 @@ In a component: * Inject . * Add a field to hold the user's identifier. -* Add a button and a method to cast the to `CustomAuthenticationStateProvider` and call `AuthenticateUser` with the user's identifier. +* Add a button and a method to cast the to `CustomAuthStateProvider` and call `AuthenticateUser` with the user's identifier. :::moniker range=">= aspnetcore-8.0" @@ -438,7 +435,7 @@ In a component: private void SignIn() { - ((CustomAuthenticationStateProvider)AuthenticationStateProvider) + ((CustomAuthStateProvider)AuthenticationStateProvider) .AuthenticateUser(userIdentifier); } } @@ -468,7 +465,7 @@ In a component: private void SignIn() { - ((CustomAuthenticationStateProvider)AuthenticationStateProvider) + ((CustomAuthStateProvider)AuthenticationStateProvider) .AuthenticateUser(userIdentifier); } } @@ -522,18 +519,18 @@ services.AddScoped(); :::moniker-end -The following `CustomAuthenticationStateProvider` subscribes to the `AuthenticationService.UserChanged` event. `GetAuthenticationStateAsync` returns the user's authentication state. Initially, the authentication state is based on the value of the `AuthenticationService.CurrentUser`. When there's a change in user, a new authentication state is created with the new user (`new AuthenticationState(newUser)`) for calls to `GetAuthenticationStateAsync`: +The following `CustomAuthStateProvider` subscribes to the `AuthenticationService.UserChanged` event. `GetAuthenticationStateAsync` returns the user's authentication state. Initially, the authentication state is based on the value of the `AuthenticationService.CurrentUser`. When there's a change in user, a new authentication state is created with the new user (`new AuthenticationState(newUser)`) for calls to `GetAuthenticationStateAsync`: ```csharp using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Components.Authorization; -public class CustomAuthenticationStateProvider : AuthenticationStateProvider +public class CustomAuthStateProvider : AuthenticationStateProvider { private AuthenticationState authenticationState; - public CustomAuthenticationStateProvider(AuthenticationService service) + public CustomAuthStateProvider(AuthenticationService service) { authenticationState = new AuthenticationState(service.CurrentUser); From e2bfa490e9313d99ac5da9467ec756cbbafcda03 Mon Sep 17 00:00:00 2001 From: Wade Pickett Date: Thu, 4 Apr 2024 11:05:38 -0700 Subject: [PATCH 2/8] Update API test tool: Custom Model Binding (#32252) * Update API test tool: Custom Model Binding * Clarified previous api * same fix on older topic version --- aspnetcore/mvc/advanced/custom-model-binding.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/aspnetcore/mvc/advanced/custom-model-binding.md b/aspnetcore/mvc/advanced/custom-model-binding.md index 29d2a37de294..792c4141d721 100644 --- a/aspnetcore/mvc/advanced/custom-model-binding.md +++ b/aspnetcore/mvc/advanced/custom-model-binding.md @@ -3,7 +3,7 @@ title: Custom Model Binding in ASP.NET Core author: ardalis description: Learn how model binding allows controller actions to work directly with model types in ASP.NET Core. ms.author: riande -ms.date: 01/06/2020 +ms.date: 04/03/2024 uid: mvc/advanced/custom-model-binding --- # Custom Model Binding in ASP.NET Core @@ -60,9 +60,7 @@ The following example shows how to use `ByteArrayModelBinder` to convert a base6 [!code-csharp[](custom-model-binding/samples/3.x/CustomModelBindingSample/Controllers/ImageController.cs?name=snippet_Post)] [!INCLUDE[about the series](~/includes/code-comments-loc.md)] -You can POST a base64-encoded string to this api method using a tool like [Postman](https://www.getpostman.com/): - -![Postman tool](custom-model-binding/images/postman.png "postman") +You can POST a base64-encoded string to the previous api method using a tool like [curl](https://curl.haxx.se/). As long as the binder can bind request data to appropriately named properties or arguments, model binding will succeed. The following example shows how to use `ByteArrayModelBinder` with a view model: @@ -183,9 +181,7 @@ The following example shows how to use `ByteArrayModelBinder` to convert a base6 [!code-csharp[](custom-model-binding/samples/2.x/CustomModelBindingSample/Controllers/ImageController.cs?name=post1)] -You can POST a base64-encoded string to this api method using a tool like [Postman](https://www.getpostman.com/): - -![Postman tool output](custom-model-binding/images/postman.png "postman") +You can POST a base64-encoded string to the previous api method using a tool like [curl](https://curl.haxx.se/). As long as the binder can bind request data to appropriately named properties or arguments, model binding will succeed. The following example shows how to use `ByteArrayModelBinder` with a view model: From bc80718f71a407e18cfb156a410412a28021af1a Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Thu, 4 Apr 2024 15:42:59 -0400 Subject: [PATCH 3/8] Clarify use of IHttpContextAccessor/HttpContext (#32260) --- aspnetcore/blazor/security/blazor-web-app-with-oidc.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/aspnetcore/blazor/security/blazor-web-app-with-oidc.md b/aspnetcore/blazor/security/blazor-web-app-with-oidc.md index 73f0b5a1be55..168a6d1789b6 100644 --- a/aspnetcore/blazor/security/blazor-web-app-with-oidc.md +++ b/aspnetcore/blazor/security/blazor-web-app-with-oidc.md @@ -48,6 +48,9 @@ The `BlazorWebAppOidc` project is the server-side project of the Blazor Web App. The `BlazorWebAppOidc.http` file can be used for testing the weather data request. Note that the `BlazorWebAppOidc` project must be running to test the endpoint, and the endpoint is hardcoded into the file. For more information, see . +> [!NOTE] +> The server project uses /, but never for interactively-rendered components. For more information, see . + ### Configuration This section explains how to configure the sample app. @@ -281,6 +284,9 @@ The `BlazorWebAppOidc` project is the server-side project of the Blazor Web App. The `BlazorWebAppOidc.http` file can be used for testing the weather data request. Note that the `BlazorWebAppOidc` project must be running to test the endpoint, and the endpoint is hardcoded into the file. For more information, see . +> [!NOTE] +> The server project uses /, but never for interactively-rendered components. For more information, see . + ### Configuration This section explains how to configure the sample app. From babeaeb967dfb66e3f45df0aeb231d5a9df2f54e Mon Sep 17 00:00:00 2001 From: Wade Pickett Date: Thu, 4 Apr 2024 21:43:53 -0700 Subject: [PATCH 4/8] SignalR: Clarify groups (#32261) * SignalR: Clarify groups * Removed h3 and fixed link. * Update groups.md Co-authored-by: Tom Dykstra * Apply suggestions from code review --------- Co-authored-by: Tom Dykstra --- aspnetcore/signalr/groups.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/aspnetcore/signalr/groups.md b/aspnetcore/signalr/groups.md index 3446d545e191..d98e24cfa896 100644 --- a/aspnetcore/signalr/groups.md +++ b/aspnetcore/signalr/groups.md @@ -5,7 +5,7 @@ description: Overview of ASP.NET Core SignalR User and Group management. monikerRange: '>= aspnetcore-2.1' ms.author: bradyg ms.custom: mvc -ms.date: 05/17/2020 +ms.date: 04/04/2024 uid: signalr/groups --- @@ -13,7 +13,7 @@ uid: signalr/groups By [Brennan Conroy](https://github.com/BrennanConroy) -SignalR allows messages to be sent to all connections associated with a specific user, as well as to named groups of connections. +SignalR allows messages to be sent to all connections associated with a specific user and to named groups of connections. [View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/signalr/groups/sample/) [(how to download)](xref:index#how-to-download-a-sample) @@ -32,13 +32,21 @@ Send a message to a specific user by passing the user identifier to the `User` f ## Groups in SignalR -A group is a collection of connections associated with a name. Messages can be sent to all connections in a group. Groups are the recommended way to send to a connection or multiple connections because the groups are managed by the application. A connection can be a member of multiple groups. Groups are ideal for something like a chat application, where each room can be represented as a group. Connections are added to or removed from groups via the `AddToGroupAsync` and `RemoveFromGroupAsync` methods. +A group is a collection of connections associated with a name. Messages can be sent to all connections in a group. Groups are the recommended way to send to a connection or multiple connections because the groups are managed by the application. A connection can be a member of multiple groups. Groups are ideal for something like a chat application, where each room can be represented as a group. + +### Add or remove connections from a group + +Connections are added to or removed from groups via the `AddToGroupAsync` and `RemoveFromGroupAsync` methods: [!code-csharp[Hub methods](groups/sample/Hubs/ChatHub.cs?range=15-27)] -Group membership isn't preserved when a connection reconnects. The connection needs to rejoin the group when it's re-established. It's not possible to count the members of a group, since this information is not available if the application is scaled to multiple servers. +It's safe to add a user to a group multiple times, no exception is thrown in the case that the user already exists in the group. + +Group membership isn't preserved when a connection reconnects. The connection needs to rejoin the group when it's re-established. It's not possible to count the members of a group, since this information isn't available if the application is scaled to multiple servers. + +Groups are kept in memory, so they won't persist through a server restart. Consider the Azure SignalR service for scenarios requiring group membership to be persisted. For more information, see [Azure SignalR](/azure/azure-signalr/signalr-overview) -To protect access to resources while using groups, use [authentication and authorization](xref:signalr/authn-and-authz) functionality in ASP.NET Core. If a user is added to a group only when the credentials are valid for that group, messages sent to that group will only go to authorized users. However, groups are not a security feature. Authentication claims have features that groups do not, such as expiry and revocation. If a user's permission to access the group is revoked, the app must remove the user from the group explicitly. +To protect access to resources while using groups, use [authentication and authorization](xref:signalr/authn-and-authz) functionality in ASP.NET Core. If a user is added to a group only when the credentials are valid for that group, messages sent to that group will only go to authorized users. However, groups are not a security feature. Authentication claims have features that groups don't, such as expiry and revocation. If a user's permission to access the group is revoked, the app must remove the user from the group explicitly. > [!NOTE] > Group names are case-sensitive. From 90f61e4aa8d6c8359443494e183e1e02b2367943 Mon Sep 17 00:00:00 2001 From: damienbod Date: Fri, 5 Apr 2024 22:40:49 +0200 Subject: [PATCH 5/8] OIDC Blazor authentication text improvements (#32265) --- .../blazor/security/blazor-web-app-with-oidc.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/aspnetcore/blazor/security/blazor-web-app-with-oidc.md b/aspnetcore/blazor/security/blazor-web-app-with-oidc.md index 168a6d1789b6..b48054528b59 100644 --- a/aspnetcore/blazor/security/blazor-web-app-with-oidc.md +++ b/aspnetcore/blazor/security/blazor-web-app-with-oidc.md @@ -147,6 +147,9 @@ The following and configuration of and : Many OIDC servers use "`name`" and "`role`" rather than the SOAP/WS-Fed defaults in . When is set to `false`, the handler doesn't perform claims mappings and the claim names from the JWT are used directly by the app. The following example manually maps the name and role claims: +> [!NOTE] +> must be set to `false` for most OIDC providers, which prevents renaming claims. + ```csharp oidcOptions.MapInboundClaims = false; oidcOptions.TokenValidationParameters.NameClaimType = JwtRegisteredClaimNames.Name; @@ -162,7 +165,7 @@ The following :::no-loc text="https://localhost/signin-oidc"::: > [!NOTE] - > A port isn't required for `localhost` addresses. + > A port isn't required for `localhost` addresses when using Microsoft Entra ID. Most other OIDC providers require a correct port. * : The request path within the app's base path where the user agent is returned after sign out from the identity provider. @@ -171,7 +174,7 @@ The following :::no-loc text="https://localhost/signout-callback-oidc"::: > [!NOTE] - > A port isn't required for `localhost` addresses. + > A port isn't required for `localhost` addresses when using Microsoft Entra ID. Most other OIDC providers require a correct port. > [!NOTE] > If using Microsoft Identity Web, the provider currently only redirects back to the if the `microsoftonline.com` Authority (`https://login.microsoftonline.com/{TENANT ID}/v2.0/`) is used. This limitation doesn't exist if you can use the "common" Authority with Microsoft Identity Web. For more information, see [postLogoutRedirectUri not working when authority url contains a tenant ID (`AzureAD/microsoft-authentication-library-for-js` #5783)](https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/5783). @@ -183,7 +186,7 @@ The following :::no-loc text="https://localhost/signout-oidc"::: > [!NOTE] - > A port isn't required for `localhost` addresses. + > A port isn't required for `localhost` addresses when using Microsoft Entra ID. Most other OIDC providers require a correct port. ```csharp oidcOptions.CallbackPath = new PathString("{PATH}"); @@ -411,6 +414,9 @@ The following and configuration of and : Many OIDC servers use "`name`" and "`role`" rather than the SOAP/WS-Fed defaults in . When is set to `false`, the handler doesn't perform claims mappings and the claim names from the JWT are used directly by the app. The following example manually maps the name and role claims: +> [!NOTE] +> must be set to `false` for most OIDC providers, which prevents renaming claims. + ```csharp oidcOptions.MapInboundClaims = false; oidcOptions.TokenValidationParameters.NameClaimType = JwtRegisteredClaimNames.Name; From ae3e5190315f589f160d2fd3c9dbc4c70e2f2f04 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Fri, 5 Apr 2024 16:53:15 -0400 Subject: [PATCH 6/8] Link update (#32267) --- aspnetcore/blazor/components/dynamiccomponent.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aspnetcore/blazor/components/dynamiccomponent.md b/aspnetcore/blazor/components/dynamiccomponent.md index c2bde59fabfb..314172a00cdd 100644 --- a/aspnetcore/blazor/components/dynamiccomponent.md +++ b/aspnetcore/blazor/components/dynamiccomponent.md @@ -145,8 +145,7 @@ In the preceding example: * An is used to manage components to be displayed. * Names serve as the dictionary keys and are provided as selection options. -* Component types are stored as dictionary values using the [`typeof` operator](/dotnet/csharp/language-reference/operators/typeof). - +* Component types are stored as dictionary values using the [`typeof` operator](/dotnet/csharp/language-reference/operators/type-testing-and-cast#typeof-operator). ## Pass parameters From c527d153e9d580e5f1230e1777cbe8cbe3534d5c Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Fri, 5 Apr 2024 19:31:03 -0400 Subject: [PATCH 7/8] Blazor WASM caching content reorganization (#32196) --- .openpublishing.redirection.json | 5 + aspnetcore/blazor/fundamentals/startup.md | 2 + .../http-caching-issues.md | 2 +- .../webassembly-caching/index.md | 163 ++++++++++++++++++ .../blazor/host-and-deploy/webassembly.md | 3 +- aspnetcore/blazor/progressive-web-app.md | 2 +- aspnetcore/migration/70-80.md | 2 +- aspnetcore/release-notes/aspnetcore-8.0.md | 2 +- aspnetcore/toc.yml | 8 +- .../whats-new/dotnet-AspNetCore.Docs-mod5.md | 2 +- 10 files changed, 182 insertions(+), 9 deletions(-) rename aspnetcore/blazor/{ => host-and-deploy/webassembly-caching}/http-caching-issues.md (98%) create mode 100644 aspnetcore/blazor/host-and-deploy/webassembly-caching/index.md diff --git a/.openpublishing.redirection.json b/.openpublishing.redirection.json index f32ad02619ef..e2d844a7646e 100644 --- a/.openpublishing.redirection.json +++ b/.openpublishing.redirection.json @@ -1277,6 +1277,11 @@ "source_path": "aspnetcore/blazor/security/server/blazor-web-app-with-oidc.md", "redirect_url": "/aspnet/core/blazor/security/blazor-web-app-with-oidc", "redirect_document_id": false + }, + { + "source_path": "aspnetcore/blazor/http-caching-issues.md", + "redirect_url": "/aspnet/core/blazor/host-and-deploy/webassembly-caching/http-caching-issues", + "redirect_document_id": false } ] } diff --git a/aspnetcore/blazor/fundamentals/startup.md b/aspnetcore/blazor/fundamentals/startup.md index b0aefcf002d5..e1ae9b718a07 100644 --- a/aspnetcore/blazor/fundamentals/startup.md +++ b/aspnetcore/blazor/fundamentals/startup.md @@ -489,6 +489,8 @@ When the `loadBootResource` function returns `null`, Blazor uses the default loa The `loadBootResource` function can also return a [`Response` promise](https://developer.mozilla.org/docs/Web/API/Response). For an example, see . +For more information, see . + ## Control headers in C# code Control headers at startup in C# code using the following approaches. diff --git a/aspnetcore/blazor/http-caching-issues.md b/aspnetcore/blazor/host-and-deploy/webassembly-caching/http-caching-issues.md similarity index 98% rename from aspnetcore/blazor/http-caching-issues.md rename to aspnetcore/blazor/host-and-deploy/webassembly-caching/http-caching-issues.md index ad86f7330f1e..d61c819a601c 100644 --- a/aspnetcore/blazor/http-caching-issues.md +++ b/aspnetcore/blazor/host-and-deploy/webassembly-caching/http-caching-issues.md @@ -6,7 +6,7 @@ monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc ms.date: 02/09/2024 -uid: blazor/http-caching-issues +uid: blazor/host-and-deploy/webassembly-caching/http-caching-issues --- # Avoid HTTP caching issues when upgrading ASP.NET Core Blazor apps diff --git a/aspnetcore/blazor/host-and-deploy/webassembly-caching/index.md b/aspnetcore/blazor/host-and-deploy/webassembly-caching/index.md new file mode 100644 index 000000000000..4077efbdcc32 --- /dev/null +++ b/aspnetcore/blazor/host-and-deploy/webassembly-caching/index.md @@ -0,0 +1,163 @@ +--- +title: ASP.NET Core Blazor WebAssembly .NET runtime and app bundle caching +author: guardrex +description: Learn how to Blazor WebAssembly caches the WebAssembly .NET runtime and app bundle, how to disable caching, and how to diagnose integrity failures. +monikerRange: '>= aspnetcore-3.1' +ms.author: riande +ms.custom: mvc +ms.date: 03/28/2024 +uid: blazor/host-and-deploy/webassembly-caching/index +--- +# ASP.NET Core Blazor WebAssembly .NET runtime and app bundle caching + +[!INCLUDE[](~/includes/not-latest-version.md)] + +When a Blazor WebAssembly app loads in the browser, the app downloads boot resources from the server: + +* JavaScript code to bootstrap the app +* .NET runtime and assemblies +* Locale specific data + +Except for Blazor's boot resources file (`blazor.boot.json`), WebAssembly .NET runtime and app bundle files are cached on clients. The `blazor.boot.json` file contains a manifest of the files that make up the app that must be downloaded along with a hash of the file's content that's used to detect whether any of the boot resources have changed. Blazor caches downloaded files using the browser [Cache](https://developer.mozilla.org/docs/Web/API/Cache) API. + +When Blazor WebAssembly downloads an app's startup files, it instructs the browser to perform integrity checks on the responses. Blazor sends SHA-256 hash values for DLL (`.dll`), WebAssembly (`.wasm`), and other files in the `blazor.boot.json` file. The file hashes of cached files are compared to the hashes in the `blazor.boot.json` file. For cached files with a matching hash, Blazor uses the cached files. Otherwise, files are requested from the server. After a file is downloaded, its hash is checked again for integrity validation. An error is generated by the browser if any downloaded file's integrity check fails. + +Blazor's algorithm for managing file integrity: + +* Ensures that the app doesn't risk loading an inconsistent set of files, for example if a new deployment is applied to your web server while the user is in the process of downloading the application files. Inconsistent files can result in a malfunctioning app. +* Ensures the user's browser never caches inconsistent or invalid responses, which can prevent the app from starting even if the user manually refreshes the page. +* Makes it safe to cache the responses and not check for server-side changes until the expected SHA-256 hashes themselves change, so subsequent page loads involve fewer requests and complete faster. + +If the web server returns responses that don't match the expected SHA-256 hashes, an error similar to the following example appears in the browser's developer console: + +> Failed to find a valid digest in the 'integrity' attribute for resource 'https://myapp.example.com/\_framework/MyBlazorApp.dll' with computed SHA-256 integrity 'IIa70iwvmEg5WiDV17OpQ5eCztNYqL186J56852RpJY='. The resource has been blocked. + +In most cases, the warning doesn't indicate a problem with integrity checking. Instead, the warning usually means that some other problem exists. + +[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] + +## Diagnosing integrity problems + +When an app is built, the generated `blazor.boot.json` manifest describes the SHA-256 hashes of boot resources at the time that the build output is produced. The integrity check passes as long as the SHA-256 hashes in `blazor.boot.json` match the files delivered to the browser. + +Common reasons why this fails include: + +* The web server's response is an error (for example, a *404 - Not Found* or a *500 - Internal Server Error*) instead of the file the browser requested. This is reported by the browser as an integrity check failure and not as a response failure. +* Something has changed the contents of the files between the build and delivery of the files to the browser. This might happen: + * If you or build tools manually modify the build output. + * If some aspect of the deployment process modified the files. For example if you use a Git-based deployment mechanism, bear in mind that Git transparently converts Windows-style line endings to Unix-style line endings if you commit files on Windows and check them out on Linux. Changing file line endings change the SHA-256 hashes. To avoid this problem, consider [using `.gitattributes` to treat build artifacts as `binary` files](https://git-scm.com/book/en/v2/Customizing-Git-Git-Attributes). + * The web server modifies the file contents as part of serving them. For example, some content distribution networks (CDNs) automatically attempt to [minify](xref:client-side/bundling-and-minification#minification) HTML, thereby modifying it. You may need to disable such features. +* The `blazor.boot.json` file fails to load properly or is improperly cached on the client. Common causes include either of the following: + * Misconfigured or malfunctioning custom developer code. + * One or more misconfigured intermediate caching layers. + +To diagnose which of these applies in your case: + + 1. Note which file is triggering the error by reading the error message. + 1. Open your browser's developer tools and look in the *Network* tab. If necessary, reload the page to see the list of requests and responses. Find the file that is triggering the error in that list. + 1. Check the HTTP status code in the response. If the server returns anything other than *200 - OK* (or another 2xx status code), then you have a server-side problem to diagnose. For example, status code 403 means there's an authorization problem, whereas status code 500 means the server is failing in an unspecified manner. Consult server-side logs to diagnose and fix the app. + 1. If the status code is *200 - OK* for the resource, look at the response content in browser's developer tools and check that the content matches up with the data expected. For example, a common problem is to misconfigure routing so that requests return your `index.html` data even for other files. Make sure that responses to `.wasm` requests are WebAssembly binaries and that responses to `.dll` requests are .NET assembly binaries. If not, you have a server-side routing problem to diagnose. + 1. Seek to validate the app's published and deployed output with the [Troubleshoot integrity PowerShell script](#troubleshoot-integrity-powershell-script). + +If you confirm that the server is returning plausibly correct data, there must be something else modifying the contents in between build and delivery of the file. To investigate this: + +* Examine the build toolchain and deployment mechanism in case they're modifying files after the files are built. An example of this is when Git transforms file line endings, as described earlier. +* Examine the web server or CDN configuration in case they're set up to modify responses dynamically (for example, trying to minify HTML). It's fine for the web server to implement HTTP compression (for example, returning `content-encoding: br` or `content-encoding: gzip`), since this doesn't affect the result after decompression. However, it's *not* fine for the web server to modify the uncompressed data. + +## Troubleshoot integrity PowerShell script + +Use the [`integrity.ps1`](https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/blazor/host-and-deploy/webassembly/_samples/integrity.ps1?raw=true) PowerShell script to validate a published and deployed Blazor app. The script is provided for PowerShell Core 7 or later as a starting point when the app has integrity issues that the Blazor framework can't identify. Customization of the script might be required for your apps, including if running on version of PowerShell later than version 7.2.0. + +The script checks the files in the `publish` folder and downloaded from the deployed app to detect issues in the different manifests that contain integrity hashes. These checks should detect the most common problems: + +* You modified a file in the published output without realizing it. +* The app wasn't correctly deployed to the deployment target, or something changed within the deployment target's environment. +* There are differences between the deployed app and the output from publishing the app. + +Invoke the script with the following command in a PowerShell command shell: + +```powershell +.\integrity.ps1 {BASE URL} {PUBLISH OUTPUT FOLDER} +``` + +In the following example, the script is executed on a locally-running app at `https://localhost:5001/`: + +```powershell +.\integrity.ps1 https://localhost:5001/ C:\TestApps\BlazorSample\bin\Release\net6.0\publish\ +``` + +Placeholders: + +* `{BASE URL}`: The URL of the deployed app. A trailing slash (`/`) is required. +* `{PUBLISH OUTPUT FOLDER}`: The path to the app's `publish` folder or location where the app is published for deployment. + +> [!NOTE] +> When cloning the `dotnet/AspNetCore.Docs` GitHub repository, the `integrity.ps1` script might be quarantined by [Bitdefender](https://www.bitdefender.com) or another virus scanner present on the system. Usually, the file is trapped by a virus scanner's *heuristic scanning* technology, which merely looks for patterns in files that might indicate the presence of malware. To prevent the virus scanner from quarantining the file, add an exception to the virus scanner prior to cloning the repo. The following example is a typical path to the script on a Windows system. Adjust the path as needed for other systems. The placeholder `{USER}` is the user's path segment. +> +> ``` +> C:\Users\{USER}\Documents\GitHub\AspNetCore.Docs\aspnetcore\blazor\host-and-deploy\webassembly\_samples\integrity.ps1 +> ``` +> +> **Warning**: *Creating virus scanner exceptions is dangerous and should only be performed when you're certain that the file is safe.* +> +> Comparing the checksum of a file to a valid checksum value doesn't guarantee file safety, but modifying a file in a way that maintains a checksum value isn't trivial for malicious users. Therefore, checksums are useful as a general security approach. Compare the checksum of the local `integrity.ps1` file to one of the following values: +> +> * SHA256: `32c24cb667d79a701135cb72f6bae490d81703323f61b8af2c7e5e5dc0f0c2bb` +> * MD5: `9cee7d7ec86ee809a329b5406fbf21a8` +> +> Obtain the file's checksum on Windows OS with the following command. Provide the path and file name for the `{PATH AND FILE NAME}` placeholder and indicate the type of checksum to produce for the `{SHA512|MD5}` placeholder, either `SHA256` or `MD5`: +> +> ```console +> CertUtil -hashfile {PATH AND FILE NAME} {SHA256|MD5} +> ``` +> +> If you have any cause for concern that checksum validation isn't secure enough in your environment, consult your organization's security leadership for guidance. +> +> For more information, see [Overview of threat protection by Microsoft Defender Antivirus](/microsoft-365/business-premium/m365bp-threats-detected-defender-av). + +## Disable resource caching and integrity checks for non-PWA apps + +In most cases, don't disable integrity checking. Disabling integrity checking doesn't solve the underlying problem that has caused the unexpected responses and results in losing the benefits listed earlier. + +There may be cases where the web server can't be relied upon to return consistent responses, and you have no choice but to temporarily disable integrity checks until the underlying problem is resolved. + +To disable integrity checks, add the following to a property group in the Blazor WebAssembly app's project file (`.csproj`): + +```xml +false +``` + +`BlazorCacheBootResources` also disables Blazor's default behavior of caching the `.dll`, `.wasm`, and other files based on their SHA-256 hashes because the property indicates that the SHA-256 hashes can't be relied upon for correctness. Even with this setting, the browser's normal HTTP cache may still cache those files, but whether or not this happens depends on your web server configuration and the `cache-control` headers that it serves. + +> [!NOTE] +> The `BlazorCacheBootResources` property doesn't disable integrity checks for [Progressive Web Applications (PWAs)](xref:blazor/progressive-web-app). For guidance pertaining to PWAs, see the [Disable integrity checking for PWAs](#disable-resource-caching-and-integrity-checks-for-pwas) section. + +We can't provide an exhaustive list of scenarios where disabling integrity checking is required. Servers can answer a request in arbitrary ways outside of the scope of the Blazor framework. The framework provides the `BlazorCacheBootResources` setting to make the app runnable at the cost of *losing a guarantee of integrity that the app can provide*. Again, we don't recommend disabling integrity checking, especially for production deployments. Developers should seek to solve the underlying integrity problem that's causing integrity checking to fail. + +A few general cases that can cause integrity issues are: + +* Running on HTTP where integrity can't be checked. +* If your deployment process modifies the files after publish in any way. +* If your host modifies the files in any way. + +## Disable resource caching and integrity checks for PWAs + +Blazor's Progressive Web Application (PWA) template contains a suggested `service-worker.published.js` file that's responsible for fetching and storing application files for offline use. This is a separate process from the normal app startup mechanism and has its own separate integrity checking logic. + +Inside the `service-worker.published.js` file, following line is present: + +```javascript +.map(asset => new Request(asset.url, { integrity: asset.hash })); +``` + +To disable integrity checking, remove the `integrity` parameter by changing the line to the following: + +```javascript +.map(asset => new Request(asset.url)); +``` + +Again, disabling integrity checking means that you lose the safety guarantees offered by integrity checking. For example, there is a risk that if the user's browser is caching the app at the exact moment that you deploy a new version, it could cache some files from the old deployment and some from the new deployment. If that happens, the app becomes stuck in a broken state until you deploy a further update. + +## Additional resources + +[Boot resource loading](xref:blazor/fundamentals/startup#load-client-side-boot-resources) diff --git a/aspnetcore/blazor/host-and-deploy/webassembly.md b/aspnetcore/blazor/host-and-deploy/webassembly.md index ac492b2a6e0b..82420f098982 100644 --- a/aspnetcore/blazor/host-and-deploy/webassembly.md +++ b/aspnetcore/blazor/host-and-deploy/webassembly.md @@ -734,7 +734,7 @@ For more information, see [`mod_mime`](https://httpd.apache.org/docs/2.4/mod/mod The default GitHub Action, which deploys pages, skips deployment of folders starting with underscore, for example, the `_framework` folder. To deploy folders starting with underscore, add an empty `.nojekyll` file to the Git branch. -Git treats JavaScript (JS) files, such as `blazor.webassembly.js`, as text and converts line endings from CRLF (carriage return-line feed) to LF (line feed) in the deployment pipeline. These changes to JS files produce different file hashes than Blazor sends to the client in the `blazor.boot.json` file. The mismatches result in integrity check failures on the client. One approach to solving this problem is to add a `.gitattributes` file with `*.js binary` line before adding the app's assets to the Git branch. The `*.js binary` line configures Git to treat JS files as binary files, which avoids processing the files in the deployment pipeline. The file hashes of the unprocessed files match the entries in the `blazor.boot.json` file, and client-side integrity checks pass. For more information, see the [Resolve integrity check failures](#resolve-integrity-check-failures) section. +Git treats JavaScript (JS) files, such as `blazor.webassembly.js`, as text and converts line endings from CRLF (carriage return-line feed) to LF (line feed) in the deployment pipeline. These changes to JS files produce different file hashes than Blazor sends to the client in the `blazor.boot.json` file. The mismatches result in integrity check failures on the client. One approach to solving this problem is to add a `.gitattributes` file with `*.js binary` line before adding the app's assets to the Git branch. The `*.js binary` line configures Git to treat JS files as binary files, which avoids processing the files in the deployment pipeline. The file hashes of the unprocessed files match the entries in the `blazor.boot.json` file, and client-side integrity checks pass. For more information, see . To handle URL rewrites, add a `wwwroot/404.html` file with a script that handles redirecting the request to the `index.html` page. For an example, see the [`SteveSandersonMS/BlazorOnGitHubPages` GitHub repository](https://github.com/SteveSandersonMS/BlazorOnGitHubPages): @@ -1287,4 +1287,3 @@ To disable integrity checking, remove the `integrity` parameter by changing the ``` Again, disabling integrity checking means that you lose the safety guarantees offered by integrity checking. For example, there is a risk that if the user's browser is caching the app at the exact moment that you deploy a new version, it could cache some files from the old deployment and some from the new deployment. If that happens, the app becomes stuck in a broken state until you deploy a further update. - diff --git a/aspnetcore/blazor/progressive-web-app.md b/aspnetcore/blazor/progressive-web-app.md index 3aca7f411b25..b5d13f2fe090 100644 --- a/aspnetcore/blazor/progressive-web-app.md +++ b/aspnetcore/blazor/progressive-web-app.md @@ -403,5 +403,5 @@ The [`CarChecker`](https://github.com/SteveSandersonMS/CarChecker) sample app de ## Additional resources -* [Troubleshoot integrity PowerShell script](xref:blazor/host-and-deploy/webassembly#troubleshoot-integrity-powershell-script) +* [Troubleshoot integrity PowerShell script](xref:blazor/host-and-deploy/webassembly-caching/index#troubleshoot-integrity-powershell-script) * [Client-side SignalR cross-origin negotiation for authentication](xref:blazor/fundamentals/signalr#client-side-signalr-cross-origin-negotiation-for-authentication) diff --git a/aspnetcore/migration/70-80.md b/aspnetcore/migration/70-80.md index e6cce645a124..3abdb85872bf 100644 --- a/aspnetcore/migration/70-80.md +++ b/aspnetcore/migration/70-80.md @@ -549,7 +549,7 @@ For more information, see the following resources: We've added a new article that discusses some of the common HTTP caching issues that can occur when upgrading Blazor apps across major versions and how to address HTTP caching issues. -For more information, see . +For more information, see . ### New article on class libraries with static server-side rendering (static SSR) diff --git a/aspnetcore/release-notes/aspnetcore-8.0.md b/aspnetcore/release-notes/aspnetcore-8.0.md index 9c8f2643830b..2ef60b48dab7 100644 --- a/aspnetcore/release-notes/aspnetcore-8.0.md +++ b/aspnetcore/release-notes/aspnetcore-8.0.md @@ -42,7 +42,7 @@ For more information, see . +For more information, see . ### New Blazor Web App template diff --git a/aspnetcore/toc.yml b/aspnetcore/toc.yml index e376e75e4446..e4e0d945d783 100644 --- a/aspnetcore/toc.yml +++ b/aspnetcore/toc.yml @@ -638,6 +638,12 @@ items: uid: blazor/host-and-deploy/server - name: Blazor WebAssembly uid: blazor/host-and-deploy/webassembly + - name: WebAssembly caching + items: + - name: Overview + uid: blazor/host-and-deploy/webassembly-caching/index + - name: HTTP caching issues + uid: blazor/host-and-deploy/webassembly-caching/http-caching-issues - name: Configure the Linker uid: blazor/host-and-deploy/configure-linker - name: Configure the Trimmer @@ -648,8 +654,6 @@ items: uid: blazor/host-and-deploy/multiple-hosted-webassembly - name: Blazor with EF Core uid: blazor/blazor-ef-core - - name: HTTP caching issues - uid: blazor/http-caching-issues - name: Advanced scenarios uid: blazor/advanced-scenarios - name: Client-side development diff --git a/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod5.md b/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod5.md index c306134d02f3..7f4a80e3da69 100644 --- a/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod5.md +++ b/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod5.md @@ -16,7 +16,7 @@ Welcome to what's new in the ASP.NET Core docs for November 2023. This article l - - - -- +- - - - From c429badd4799dfbbddb1c67cdc5e85cd704d13a7 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Fri, 5 Apr 2024 19:34:56 -0400 Subject: [PATCH 8/8] WASM runtime max heap size (#32257) --- .../blazor/host-and-deploy/webassembly.md | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/aspnetcore/blazor/host-and-deploy/webassembly.md b/aspnetcore/blazor/host-and-deploy/webassembly.md index 82420f098982..9b4c69ca08da 100644 --- a/aspnetcore/blazor/host-and-deploy/webassembly.md +++ b/aspnetcore/blazor/host-and-deploy/webassembly.md @@ -132,6 +132,26 @@ Disable the trimming property if it prevents your app from running normally: :::moniker-end +## Decrease maximum heap size for some mobile device browsers + +:::moniker range=">= aspnetcore-8.0" + +When building a Blazor app that runs on the client (`.Client` project of a Blazor Web App or standalone Blazor WebAssembly app) and targets mobile device browsers, especially Safari on iOS, decreasing the maximum memory for the app with the MSBuild property `EmccMaximumHeapSize` may be required. The default value is 2,147,483,648 bytes, which may be too large and result in the app crashing if the app attempts to allocate more memory with the browser failing to grant it. The following example sets the value to 268,435,456 bytes in the `Program` file: + +:::moniker-end + +:::moniker range="< aspnetcore-8.0" + +When building a Blazor WebAssembly app that targets mobile device browsers, especially Safari on iOS, decreasing the maximum memory for the app with the MSBuild property `EmccMaximumHeapSize` may be required. The default value is 2,147,483,648 bytes, which may be too large and result in the app crashing if the app attempts to allocate more memory with the browser failing to grant it. The following example sets the value to 268,435,456 bytes in the `Program` file: + +:::moniker-end + +```xml +268435456 +``` + +For more information on [Mono](https://github.com/mono/mono)/WebAssembly MSBuild properties and targets, see [`WasmApp.Common.targets` (`dotnet/runtime` GitHub repository)](https://github.com/dotnet/runtime/blob/main/src/mono/wasm/build/WasmApp.Common.targets). + :::moniker range=">= aspnetcore-6.0" ## Runtime relinking