03-11-2020, 06:13 AM
Blazor WebAssembly 3.2.0 Preview 2 release now available
<div style="margin: 5px 5% 10px 5%;"><img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available.jpg" width="150" height="150" title="" alt="" /></div><div><div class="row justify-content-center">
<div class="col-md-4">
<div><img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available.jpg" width="58" height="58" alt="Daniel Roth" class="avatar avatar-58 wp-user-avatar wp-user-avatar-58 alignnone photo"></p>
<p>Daniel</p>
</div>
</div>
</div>
<div class="entry-meta">
<p>March 10th, 2020</p>
</p></div>
<p><!-- .entry-meta --> </p>
<p>A new preview update of Blazor WebAssembly is now available! Here’s what’s new in this release:</p>
<ul>
<li>Integration with ASP.NET Core static web assets</li>
<li>Token-based authentication</li>
<li>Improved framework caching</li>
<li>Updated linker configuration</li>
<li>Build Progressive Web Apps</li>
</ul>
<h2>Get started</h2>
<p>To get started with Blazor WebAssembly 3.2.0 Preview 2 install the latest <a href="https://dotnet.microsoft.com/download/dotnet-core/3.1">.NET Core 3.1 SDK</a>.</p>
<blockquote>
<p>NOTE: Version 3.1.102 or later of the .NET Core SDK is <strong>required</strong> to use this Blazor WebAssembly release! Make sure you have the correct .NET Core SDK version by running <code>dotnet --version</code> from a command prompt.</p>
</blockquote>
<p>Once you have the appropriate .NET Core SDK installed, run the following command to install the update Blazor WebAssembly template:</p>
<pre><code>dotnet new -i Microsoft.AspNetCore.Components.WebAssembly.Templates::3.2.0-preview2.20160.5
</code></pre>
<p>That’s it! You can find additional docs and samples on <a href="https://blazor.net">https://blazor.net</a>.</p>
<h2>Upgrade an existing project</h2>
<p>To upgrade an existing Blazor WebAssembly app from 3.2.0 Preview 1 to 3.2.0 Preview 2:</p>
<ul>
<li>Update your package references and namespaces as described below:
<ul>
<li>Microsoft.AspNetCore.Blazor -> Microsoft.AspNetCore.Components.WebAssembly</li>
<li>Microsoft.AspNetCore.Blazor.Build -> Microsoft.AspNetCore.Components.WebAssembly.Build</li>
<li>Microsoft.AspNetCore.Blazor.DevServer -> Microsoft.AspNetCore.Components.WebAssembly.DevServer</li>
<li>Microsoft.AspNetCore.Blazor.Server -> Microsoft.AspNetCore.Components.WebAssembly.Server</li>
<li>Microsoft.AspNetCore.Blazor.HttpClient -> Microsoft.AspNetCore.Blazor.HttpClient (unchanged)</li>
<li>Microsoft.AspNetCore.Blazor.DataAnnotations.Validation -> Microsoft.AspNetCore.Components.DataAnnotations.Validation</li>
<li>Microsoft.AspNetCore.Blazor.Mono -> Microsoft.AspNetCore.Components.WebAssembly.Runtime</li>
<li>Mono.WebAssembly.Interop -> Microsoft.JSInterop.WebAssembly</li>
</ul>
</li>
<li>Update all Microsoft.AspNetCore.Components.WebAssembly.* package references to version 3.2.0-preview2.20160.5.</li>
<li>In <em>Program.cs</em> add a call to <code>builder.Services.AddBaseAddressHttpClient()</code>.</li>
<li>Rename <code>BlazorLinkOnBuild</code> in your project files to <code>BlazorWebAssemblyEnableLinking</code>.</li>
<li>If your Blazor WebAssembly app is hosted using ASP.NET Core, make the following updates in <em>Startup.cs</em> in your Server project:
<ul>
<li>Rename <code>UseBlazorDebugging</code> to <code>UseWebAssemblyDebugging</code>.</li>
<li>Remove the call to <code>services.AddResponseCompression</code> (response compression is now handled by the Blazor framework).</li>
<li>Replace the call to <code>app.UseClientSideBlazorFiles<Client.Program>()</code> with <code>app.UseBlazorFrameworkFiles()</code>.</li>
<li>Replace the call to <code>endpoints.MapFallbackToClientSideBlazor<Client.Program>("index.html")</code> with <code>endpoints.MapFallbackToFile("index.html")</code>.</li>
</ul>
</li>
</ul>
<p>Hopefully that wasn’t too painful!</p>
<h2>Integration with ASP.NET Core static web assets</h2>
<p>Blazor WebAssembly apps now integrate seamlessly with how ASP.NET Core handles static web assets. Blazor WebAssembly apps can use the standard ASP.NET Core convention for consuming static web assets from referenced projects and packages: <em>_content/{LIBRARY NAME}/{path}</em>. This allows you to easily pick up static assets from referenced component libraries and JavaScript interop libraries just like you can in a Blazor Server app or any other ASP.NET Core web app.</p>
<p>Blazor WebAssembly apps are also now hosted in ASP.NET Core web apps using the same static web assets infrastructure. After all, a Blazor WebAssembly app is just a bunch of static files!</p>
<p>This integration simplifies the startup code for ASP.NET Core hosted Blazor WebAssembly apps and removes the need to have an assembly reference from the server project to the client project. Only the project reference is needed, so setting <code>ReferenceOutputAssembly</code> to <code>false</code> for the client project reference is now supported.</p>
<p>Building on the static web assets support in ASP.NET Core also enables new scenarios like hosting ASP.NET Core hosted Blazor WebAssembly apps in Docker containers. In Visual Studio you can add docker support to your Blazor WebAssembly app by right-clicking on the Server project and selecting Add > Docker support.</p>
<h2>Token-based authentication</h2>
<p>Blazor WebAssembly now has built-in support for token-based authentication.</p>
<p>Blazor WebAssembly apps are secured in the same manner as Single Page Applications (SPAs). There are several approaches for authenticating users to SPAs, but the most common and comprehensive approach is to use an implementation based on the OAuth 2.0 protocol, such as OpenID Connect (OIDC). OIDC allows client apps, like a Blazor WebAssembly app, to verify the user identity and obtain basic profile information using a trusted provider.</p>
<p>Using the Blazor WebAssembly project template you can now quickly create apps setup for authentication using:</p>
<ul>
<li>ASP.NET Core Identity and IdentityServer</li>
<li>An existing OpenID Connect provider</li>
<li>Azure Active Directory</li>
</ul>
<h3>Authenticate using ASP.NET Core Identity and IdentityServer</h3>
<p>Authentication for Blazor WebAssembly apps can be handled using ASP.NET Core Identity and IdentityServer. ASP.NET Core Identity handles authenticating users while IdentityServer handles the necessary protocol endpoints.</p>
<p>To create a Blazor WebAssembly app setup with authentication using ASP.NET Core Identity and IdentityServer run the following command:</p>
<pre><code>dotnet new blazorwasm --hosted --auth Individual -o BlazorAppWithAuth1
</code></pre>
<p>If you’re using Visual Studio, you can create the project by selecting the “ASP.NET Core hosted” option and the selecting “Change Authentication” > “Individual user accounts”.</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available.png" alt="Blazor authentication"></a></p>
<p>Run the app and try to access the Fetch Data page. You’ll get redirected to the login page.</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-1.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-1.png" alt="Blazor login"></a></p>
<p>Register a new user and log in. You can now access the Fetch Data page.</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-2.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-2.png" alt="Fetch data authorized"></a></p>
<p>The Server project is configured to use the default ASP.NET Core Identity UI, as well as IdentityServer, and JWT authentication:</p>
<pre><code class="csharp">// Add the default ASP.NET Core Identity UI
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>(); // Add IdentityServer with support for API authorization
services.AddIdentityServer() .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(); // Add JWT authentication
services.AddAuthentication() .AddIdentityServerJwt();
</code></pre>
<p>The Client app is registered with IdentityServer in the <em>appsettings.json</em> file:</p>
<pre><code class="json">"IdentityServer": { "Clients": { "BlazorAppWithAuth1.Client": { "Profile": "IdentityServerSPA" } }
},
</code></pre>
<p>In the Client project, the services needed for API authorization are added in <em>Program.cs</em>:</p>
<pre><code class="csharp">builder.Services.AddApiAuthorization();
</code></pre>
<p>In <em>FetchData.razor</em> the <code>IAccessTokenProvider</code> service is used to acquire an access token from the server. The token may be cached or acquired without the need of user interaction. If acquiring the token succeeds, it is then applied to the request for weather forecast data using the standard HTTP Authorization header. If acquiring the token silently fails, the user is redirected to the login page:</p>
<pre><code class="csharp">protected override async Task OnInitializedAsync()
{ var httpClient = new HttpClient(); httpClient.BaseAddress = new Uri(Navigation.BaseUri); var tokenResult = await AuthenticationService.RequestAccessToken(); if (tokenResult.TryGetToken(out var token)) { httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token.Value}"); forecasts = await httpClient.GetJsonAsync<WeatherForecast[]>("WeatherForecast"); } else { Navigation.NavigateTo(tokenResult.RedirectUrl); }
}
</code></pre>
<p>For additional details on using Blazor WebAssembly with ASP.NET Core Identity and IdentityServer see <a href="https://docs.microsoft.com/aspnet/core/security/blazor/webassembly/hosted-with-identity-server">Secure an ASP.NET Core Blazor WebAssembly hosted app with Identity Server</a></p>
<h3>Authenticate using an existing OpenID Connect provider</h3>
<p>You can setup authentication for a standalone Blazor WebAssembly using any valid OIDC provider. Once you’ve registered your app with the OIDC provider you configure the Blazor WebAssembly app to use that provider by calling <code>AddOidcAuthentication</code> in <em>Program.cs</em>:</p>
<pre><code class="csharp">builder.Services.AddOidcAuthentication(options =>
{ options.ProviderOptions.Authority = "{AUTHORITY}"; options.ProviderOptions.ClientId = "{CLIENT ID}";
});
</code></pre>
<p>You can create a standalone Blazor WebAssembly app that uses a specific OIDC provider for authentication using the following command:</p>
<pre><code>dotnet new blazorwasm --auth Individual --client-id "{CLIENT ID}" --authority "{AUTHORITY}"
</code></pre>
<p>For more details on configuring authentication for a standalone Blazor WebAssembly app see <a href="https://docs.microsoft.com/aspnet/core/security/blazor/webassembly/standalone-with-authentication-library">Secure an ASP.NET Core Blazor WebAssembly standalone app with the Authentication library</a></p>
<h3>Authenticate using Azure AD & Azure AD B2C</h3>
<p>You can also setup Blazor WebAssembly apps to use Azure Active Directory (Azure AD) or Azure Active Directory Business-to-Customer (Azure AD B2C) for authentication. When authenticating using Azure AD or Azure AD B2C authentication is handled using the new Microsoft.Authentication.WebAssembly.Msal library, which is based on the Microsoft Authentication Library (MSAL.js).</p>
<p>To learn how to setup authentication for Blazor WebAssembly app using Azure AD or Azure AD B2C see:</p>
<h3>Additional authentication resources</h3>
<p>This is just a sampling of the new authentication capabilities in Blazor WebAssembly. To learn more about how Blazor WebAssembly supports authentication see <a href="https://docs.microsoft.com/aspnet/core/security/blazor/webassembly/">Secure ASP.NET Core Blazor WebAssembly</a>.</p>
<h2>Improved framework caching</h2>
<p>If you look at the network trace of what’s being download for a Blazor WebAssembly app after it’s initially loaded, you might think that Blazor WebAssembly has been put on some sort of extreme diet:</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-3.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-3.png" alt="Blazor network"></a></p>
<p>Whoa! Only 159kB? What’s going on here?</p>
<p>When a Blazor WebAssembly app is initially loaded, the runtime and framework files are now stored in the browser cache storage:</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-4.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-4.png" alt="Blazor cache"></a></p>
<p>When the app loads, it first uses the contents of the <em>blazor.boot.json</em> to check if it already has all of the runtime and framework files it needs in the cache. If it does, then no additional network requests are necessary.</p>
<p>You can still see what the true size of the app is during development by checking the browser console:</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-5.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-5.png" alt="Blazor loaded resources"></a></p>
<h2>Updated linker configuration</h2>
<p>You may notice with this preview release that the download size of the app during development is now a bit larger, but build times are faster. This is because we no longer run the .NET IL linker during development to remove unused code. In previous Blazor previews we ran the linker on every build, which slowed down development. Now we only run the linker for release builds, which are typically done as part of publishing the app. When publishing the app with a release build (<code>dotnet publish -c Release</code>), the linker removes any unnecessary code and the download size is much more reasonable (~2MB for the default template).</p>
<p>If you prefer to still run the .NET IL linker on each build during development, you can turn it on by adding <code><BlazorWebAssemblyEnableLinking>true<BlazorWebAssemblyEnableLinking></code> to your project file.</p>
<h2>Build Progressive Web Apps with Blazor</h2>
<p>A Progressive Web App (PWA) is a web-based app that uses modern browser APIs and capabilities to behave like a native app. These capabilities can include:</p>
<ul>
<li>Working offline and always loading instantly, independently of network speed</li>
<li>Being able to run in its own app window, not just a browser window</li>
<li>Being launched from the host operating system (OS) start menu, dock, or home screen</li>
<li>Receiving push notifications from a backend server, even while the user is not using the app</li>
<li>Automatically updating in the background</li>
</ul>
<p>A user might first discover and use the app within their web browser like any other single-page app (SPA), then later progress to installing it in their OS and enabling push notifications.</p>
<p>Blazor WebAssembly is a true standards-based client-side web app platform, so it can use any browser API, including the APIs needed for PWA functionality.</p>
<h3>Using the PWA template</h3>
<p>When creating a new Blazor WebAssembly app, you’re offered the option to add PWA features. In Visual Studio, the option is given as a checkbox in the project creation dialog:</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-6.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-6.png" alt="image"></a></p>
<p>If you’re creating the project on the command line, you can use the <code>--pwa</code> flag. For example,</p>
<pre><code>dotnet new blazorwasm --pwa -o MyNewProject
</code></pre>
<p>In both cases, you’re free to also use the “ASP.NET Core hosted” option if you wish, but don’t have to do so. PWA features are independent of how the app is hosted.</p>
<h3>Installation and app manifest</h3>
<p>When visiting an app created using the PWA template option, users have the option to install the app into their OS’s start menu, dock, or home screen.</p>
<p>The way this option is presented depends on the user’s browser. For example, when using desktop Chromium-based browsers such as Edge or Chrome, an <em>Add</em> button appears within the address bar:</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-7.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-7.png" alt="image"></a></p>
<p>On iOS, visitors can install the PWA using Safari’s <em>Share</em> button and its <em>Add to Homescreen</em> option. On Chrome for Android, users should tap the <em>Menu</em> button in the upper-right corner, then choose <em>Add to Home screen</em>.</p>
<p>Once installed, the app appears in its own window, without any address bar.</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-8.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-8.png" alt="image"></a></p>
<p>To customize the window’s title, color scheme, icon, or other details, see the file <code>manifest.json</code> in your project’s <code>wwwroot</code> directory. The schema of this file is defined by web standards. For detailed documentation, see https://developer.mozilla.org/en-US/docs...nifest.</p>
<h3>Offline support</h3>
<p>By default, apps created using the PWA template option have support for running offline. A user must first visit the app while they are online, then the browser will automatically download and cache all the resources needed to operate offline.</p>
<blockquote>
<p><strong>Important:</strong> Offline support is only enabled for <em>published</em> apps. It is not enabled during development. This is because it would interfere with the usual development cycle of making changes and testing them.</p>
<p><strong>Warning:</strong> If you intend to ship an offline-enabled PWA, there are <a href="https://aka.ms/blazor-offline-considerations">several important warnings and caveats</a> you need to understand. These are inherent to offline PWAs, and not specific to Blazor. Be sure to read and understand these caveats before making assumptions about how your offline-enabled app will work.</p>
</blockquote>
<p>To see how offline support works, first <a href="https://docs.microsoft.com/aspnet/core/host-and-deploy/blazor/#publish-the-app">publish your app</a>, and host it on a server supporting HTTPS. When you visit the app, you should be able to open the browser’s dev tools and verify that a <em>Service Worker</em> is registered for your host:</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-9.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-9.png" alt="image"></a></p>
<p>Additionally, if you reload the page, then on the <em>Network</em> tab you should see that all resources needed to load your page are being retrieved from the <em>Service Worker</em> or <em>Memory Cache</em>:</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-10.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-10.png" alt="image"></a></p>
<p>This shows that the browser is not dependent on network access to load your app. To verify this, you can either shut down your web server, or instruct the browser to simulate offline mode:</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-11.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-11.png" alt="image"></a></p>
<p>Now, even without access to your web server, you should be able to reload the page and see that your app still loads and runs. Likewise, even if you simulate a very slow network connection, your page will still load almost immediately since it’s loaded independently of the network.</p>
<p>To learn more about building PWAs with Blazor, check out the <a href="https://docs.microsoft.com/aspnet/core/blazor/progressive-web-app/">documentation</a>.</p>
<h2>Known issues</h2>
<p>There are a few known issues with this release that you may run into:</p>
<ul>
<li>
<p>When building a Blazor WebAssembly app using an older .NET Core SDK you may see the following build error:</p>
<pre><code>error MSB4018: The "ResolveBlazorRuntimeDependencies" task failed unexpectedly.
error MSB4018: System.IO.FileNotFoundException: Could not load file or assembly '\BlazorApp1\obj\Debug\netstandard2.1\BlazorApp1.dll'. The system cannot find the file specified.
error MSB4018: File name: '\BlazorApp1\obj\Debug\netstandard2.1\BlazorApp1.dll' error MSB4018: at System.Reflection.AssemblyName.nGetFileInformation(String s)
error MSB4018: at System.Reflection.AssemblyName.GetAssemblyName(String assemblyFile)
error MSB4018: at Microsoft.AspNetCore.Components.WebAssembly.Build.ResolveBlazorRuntimeDependencies.GetAssemblyName(String assemblyPath)
error MSB4018: at Microsoft.AspNetCore.Components.WebAssembly.Build.ResolveBlazorRuntimeDependencies.ResolveRuntimeDependenciesCore(String entryPoint, IEnumerable`1 applicationDependencies, IEnumerable`1 monoBclAssemblies)
error MSB4018: at Microsoft.AspNetCore.Components.WebAssembly.Build.ResolveBlazorRuntimeDependencies.Execute()
error MSB4018: at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
error MSB4018: at Microsoft.Build.BackEnd.TaskBuilder.ExecuteInstantiatedTask(ITaskExecutionHost taskExecutionHost, TaskLoggingContext taskLoggingContext, TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteTask)
</code></pre>
<p>To address this issue, upgrade to version 3.1.102 or later of the <a href="https://dotnet.microsoft.com/download/dotnet-core/3.1">.NET Core 3.1 SDK</a>.</p>
</li>
<li>
<p>You may see the following warning when building from the command-line:</p>
<pre><code>CSC : warning CS8034: Unable to load Analyzer assembly C:\Users\user\.nuget\packages\microsoft.aspnetcore.components.analyzers\3.1.0\analyzers\dotnet\cs\Microsoft.AspNetCore.Components.Analyzers.dll : Assembly with same name is already loaded
</code></pre>
<p>This issue will be fixed in a future update to the .NET Core SDK. To workaround this issue, add the <code><DisableImplicitComponentsAnalyzers>true</DisableImplicitComponentsAnalyzers></code> property to the project file.</p>
</li>
</ul>
<h2>Feedback</h2>
<p>We hope you enjoy the new features in this preview release of Blazor WebAssembly! Please let us know what you think by filing issues on <a href="https://github.com/dotnet/aspnetcore/issues">GitHub</a>.</p>
<p>Thanks for trying out Blazor!</p>
<div class="authorinfoarea">
<div><img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available.jpg" width="96" height="96" alt="Daniel Roth" class="avatar avatar-96 wp-user-avatar wp-user-avatar-96 alignnone photo"></div>
<div>
<h5><a class="no-underline" aria-label="Daniel Roth is the name of the author of this post. Click to access author page." href="https://devblogs.microsoft.com/aspnet/author/danroth27/">Daniel Roth</a></h5>
<p>Principal Program Manager, ASP.NET</p>
<p><strong>Follow Daniel</strong> <a class="no-underline stayinformed" aria-label="Daniel Roth Twitter profile" target="_blank" href="https://twitter.com/danroth27" rel="noopener noreferrer"><i class="fa fa-twitter"></i></a><a class="no-underline stayinformed" aria-label="Daniel Roth GitHub profile" target="_blank" href="https://github.com/danroth27" rel="noopener noreferrer"><i class="fa fa-github"></i></a><a class="no-underline stayinformed hvr-pop" aria-label="Daniel Roth RSS Feed" target="_blank" href="https://devblogs.microsoft.com/aspnet/author/danroth27/feed/" rel="noopener noreferrer"></a></p>
</p></div>
</p></div>
</div>
https://www.sickgaming.net/blog/2020/03/...available/
<div style="margin: 5px 5% 10px 5%;"><img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available.jpg" width="150" height="150" title="" alt="" /></div><div><div class="row justify-content-center">
<div class="col-md-4">
<div><img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available.jpg" width="58" height="58" alt="Daniel Roth" class="avatar avatar-58 wp-user-avatar wp-user-avatar-58 alignnone photo"></p>
<p>Daniel</p>
</div>
</div>
</div>
<div class="entry-meta">
<p>March 10th, 2020</p>
</p></div>
<p><!-- .entry-meta --> </p>
<p>A new preview update of Blazor WebAssembly is now available! Here’s what’s new in this release:</p>
<ul>
<li>Integration with ASP.NET Core static web assets</li>
<li>Token-based authentication</li>
<li>Improved framework caching</li>
<li>Updated linker configuration</li>
<li>Build Progressive Web Apps</li>
</ul>
<h2>Get started</h2>
<p>To get started with Blazor WebAssembly 3.2.0 Preview 2 install the latest <a href="https://dotnet.microsoft.com/download/dotnet-core/3.1">.NET Core 3.1 SDK</a>.</p>
<blockquote>
<p>NOTE: Version 3.1.102 or later of the .NET Core SDK is <strong>required</strong> to use this Blazor WebAssembly release! Make sure you have the correct .NET Core SDK version by running <code>dotnet --version</code> from a command prompt.</p>
</blockquote>
<p>Once you have the appropriate .NET Core SDK installed, run the following command to install the update Blazor WebAssembly template:</p>
<pre><code>dotnet new -i Microsoft.AspNetCore.Components.WebAssembly.Templates::3.2.0-preview2.20160.5
</code></pre>
<p>That’s it! You can find additional docs and samples on <a href="https://blazor.net">https://blazor.net</a>.</p>
<h2>Upgrade an existing project</h2>
<p>To upgrade an existing Blazor WebAssembly app from 3.2.0 Preview 1 to 3.2.0 Preview 2:</p>
<ul>
<li>Update your package references and namespaces as described below:
<ul>
<li>Microsoft.AspNetCore.Blazor -> Microsoft.AspNetCore.Components.WebAssembly</li>
<li>Microsoft.AspNetCore.Blazor.Build -> Microsoft.AspNetCore.Components.WebAssembly.Build</li>
<li>Microsoft.AspNetCore.Blazor.DevServer -> Microsoft.AspNetCore.Components.WebAssembly.DevServer</li>
<li>Microsoft.AspNetCore.Blazor.Server -> Microsoft.AspNetCore.Components.WebAssembly.Server</li>
<li>Microsoft.AspNetCore.Blazor.HttpClient -> Microsoft.AspNetCore.Blazor.HttpClient (unchanged)</li>
<li>Microsoft.AspNetCore.Blazor.DataAnnotations.Validation -> Microsoft.AspNetCore.Components.DataAnnotations.Validation</li>
<li>Microsoft.AspNetCore.Blazor.Mono -> Microsoft.AspNetCore.Components.WebAssembly.Runtime</li>
<li>Mono.WebAssembly.Interop -> Microsoft.JSInterop.WebAssembly</li>
</ul>
</li>
<li>Update all Microsoft.AspNetCore.Components.WebAssembly.* package references to version 3.2.0-preview2.20160.5.</li>
<li>In <em>Program.cs</em> add a call to <code>builder.Services.AddBaseAddressHttpClient()</code>.</li>
<li>Rename <code>BlazorLinkOnBuild</code> in your project files to <code>BlazorWebAssemblyEnableLinking</code>.</li>
<li>If your Blazor WebAssembly app is hosted using ASP.NET Core, make the following updates in <em>Startup.cs</em> in your Server project:
<ul>
<li>Rename <code>UseBlazorDebugging</code> to <code>UseWebAssemblyDebugging</code>.</li>
<li>Remove the call to <code>services.AddResponseCompression</code> (response compression is now handled by the Blazor framework).</li>
<li>Replace the call to <code>app.UseClientSideBlazorFiles<Client.Program>()</code> with <code>app.UseBlazorFrameworkFiles()</code>.</li>
<li>Replace the call to <code>endpoints.MapFallbackToClientSideBlazor<Client.Program>("index.html")</code> with <code>endpoints.MapFallbackToFile("index.html")</code>.</li>
</ul>
</li>
</ul>
<p>Hopefully that wasn’t too painful!</p>
<h2>Integration with ASP.NET Core static web assets</h2>
<p>Blazor WebAssembly apps now integrate seamlessly with how ASP.NET Core handles static web assets. Blazor WebAssembly apps can use the standard ASP.NET Core convention for consuming static web assets from referenced projects and packages: <em>_content/{LIBRARY NAME}/{path}</em>. This allows you to easily pick up static assets from referenced component libraries and JavaScript interop libraries just like you can in a Blazor Server app or any other ASP.NET Core web app.</p>
<p>Blazor WebAssembly apps are also now hosted in ASP.NET Core web apps using the same static web assets infrastructure. After all, a Blazor WebAssembly app is just a bunch of static files!</p>
<p>This integration simplifies the startup code for ASP.NET Core hosted Blazor WebAssembly apps and removes the need to have an assembly reference from the server project to the client project. Only the project reference is needed, so setting <code>ReferenceOutputAssembly</code> to <code>false</code> for the client project reference is now supported.</p>
<p>Building on the static web assets support in ASP.NET Core also enables new scenarios like hosting ASP.NET Core hosted Blazor WebAssembly apps in Docker containers. In Visual Studio you can add docker support to your Blazor WebAssembly app by right-clicking on the Server project and selecting Add > Docker support.</p>
<h2>Token-based authentication</h2>
<p>Blazor WebAssembly now has built-in support for token-based authentication.</p>
<p>Blazor WebAssembly apps are secured in the same manner as Single Page Applications (SPAs). There are several approaches for authenticating users to SPAs, but the most common and comprehensive approach is to use an implementation based on the OAuth 2.0 protocol, such as OpenID Connect (OIDC). OIDC allows client apps, like a Blazor WebAssembly app, to verify the user identity and obtain basic profile information using a trusted provider.</p>
<p>Using the Blazor WebAssembly project template you can now quickly create apps setup for authentication using:</p>
<ul>
<li>ASP.NET Core Identity and IdentityServer</li>
<li>An existing OpenID Connect provider</li>
<li>Azure Active Directory</li>
</ul>
<h3>Authenticate using ASP.NET Core Identity and IdentityServer</h3>
<p>Authentication for Blazor WebAssembly apps can be handled using ASP.NET Core Identity and IdentityServer. ASP.NET Core Identity handles authenticating users while IdentityServer handles the necessary protocol endpoints.</p>
<p>To create a Blazor WebAssembly app setup with authentication using ASP.NET Core Identity and IdentityServer run the following command:</p>
<pre><code>dotnet new blazorwasm --hosted --auth Individual -o BlazorAppWithAuth1
</code></pre>
<p>If you’re using Visual Studio, you can create the project by selecting the “ASP.NET Core hosted” option and the selecting “Change Authentication” > “Individual user accounts”.</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available.png" alt="Blazor authentication"></a></p>
<p>Run the app and try to access the Fetch Data page. You’ll get redirected to the login page.</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-1.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-1.png" alt="Blazor login"></a></p>
<p>Register a new user and log in. You can now access the Fetch Data page.</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-2.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-2.png" alt="Fetch data authorized"></a></p>
<p>The Server project is configured to use the default ASP.NET Core Identity UI, as well as IdentityServer, and JWT authentication:</p>
<pre><code class="csharp">// Add the default ASP.NET Core Identity UI
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>(); // Add IdentityServer with support for API authorization
services.AddIdentityServer() .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(); // Add JWT authentication
services.AddAuthentication() .AddIdentityServerJwt();
</code></pre>
<p>The Client app is registered with IdentityServer in the <em>appsettings.json</em> file:</p>
<pre><code class="json">"IdentityServer": { "Clients": { "BlazorAppWithAuth1.Client": { "Profile": "IdentityServerSPA" } }
},
</code></pre>
<p>In the Client project, the services needed for API authorization are added in <em>Program.cs</em>:</p>
<pre><code class="csharp">builder.Services.AddApiAuthorization();
</code></pre>
<p>In <em>FetchData.razor</em> the <code>IAccessTokenProvider</code> service is used to acquire an access token from the server. The token may be cached or acquired without the need of user interaction. If acquiring the token succeeds, it is then applied to the request for weather forecast data using the standard HTTP Authorization header. If acquiring the token silently fails, the user is redirected to the login page:</p>
<pre><code class="csharp">protected override async Task OnInitializedAsync()
{ var httpClient = new HttpClient(); httpClient.BaseAddress = new Uri(Navigation.BaseUri); var tokenResult = await AuthenticationService.RequestAccessToken(); if (tokenResult.TryGetToken(out var token)) { httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token.Value}"); forecasts = await httpClient.GetJsonAsync<WeatherForecast[]>("WeatherForecast"); } else { Navigation.NavigateTo(tokenResult.RedirectUrl); }
}
</code></pre>
<p>For additional details on using Blazor WebAssembly with ASP.NET Core Identity and IdentityServer see <a href="https://docs.microsoft.com/aspnet/core/security/blazor/webassembly/hosted-with-identity-server">Secure an ASP.NET Core Blazor WebAssembly hosted app with Identity Server</a></p>
<h3>Authenticate using an existing OpenID Connect provider</h3>
<p>You can setup authentication for a standalone Blazor WebAssembly using any valid OIDC provider. Once you’ve registered your app with the OIDC provider you configure the Blazor WebAssembly app to use that provider by calling <code>AddOidcAuthentication</code> in <em>Program.cs</em>:</p>
<pre><code class="csharp">builder.Services.AddOidcAuthentication(options =>
{ options.ProviderOptions.Authority = "{AUTHORITY}"; options.ProviderOptions.ClientId = "{CLIENT ID}";
});
</code></pre>
<p>You can create a standalone Blazor WebAssembly app that uses a specific OIDC provider for authentication using the following command:</p>
<pre><code>dotnet new blazorwasm --auth Individual --client-id "{CLIENT ID}" --authority "{AUTHORITY}"
</code></pre>
<p>For more details on configuring authentication for a standalone Blazor WebAssembly app see <a href="https://docs.microsoft.com/aspnet/core/security/blazor/webassembly/standalone-with-authentication-library">Secure an ASP.NET Core Blazor WebAssembly standalone app with the Authentication library</a></p>
<h3>Authenticate using Azure AD & Azure AD B2C</h3>
<p>You can also setup Blazor WebAssembly apps to use Azure Active Directory (Azure AD) or Azure Active Directory Business-to-Customer (Azure AD B2C) for authentication. When authenticating using Azure AD or Azure AD B2C authentication is handled using the new Microsoft.Authentication.WebAssembly.Msal library, which is based on the Microsoft Authentication Library (MSAL.js).</p>
<p>To learn how to setup authentication for Blazor WebAssembly app using Azure AD or Azure AD B2C see:</p>
<h3>Additional authentication resources</h3>
<p>This is just a sampling of the new authentication capabilities in Blazor WebAssembly. To learn more about how Blazor WebAssembly supports authentication see <a href="https://docs.microsoft.com/aspnet/core/security/blazor/webassembly/">Secure ASP.NET Core Blazor WebAssembly</a>.</p>
<h2>Improved framework caching</h2>
<p>If you look at the network trace of what’s being download for a Blazor WebAssembly app after it’s initially loaded, you might think that Blazor WebAssembly has been put on some sort of extreme diet:</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-3.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-3.png" alt="Blazor network"></a></p>
<p>Whoa! Only 159kB? What’s going on here?</p>
<p>When a Blazor WebAssembly app is initially loaded, the runtime and framework files are now stored in the browser cache storage:</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-4.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-4.png" alt="Blazor cache"></a></p>
<p>When the app loads, it first uses the contents of the <em>blazor.boot.json</em> to check if it already has all of the runtime and framework files it needs in the cache. If it does, then no additional network requests are necessary.</p>
<p>You can still see what the true size of the app is during development by checking the browser console:</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-5.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-5.png" alt="Blazor loaded resources"></a></p>
<h2>Updated linker configuration</h2>
<p>You may notice with this preview release that the download size of the app during development is now a bit larger, but build times are faster. This is because we no longer run the .NET IL linker during development to remove unused code. In previous Blazor previews we ran the linker on every build, which slowed down development. Now we only run the linker for release builds, which are typically done as part of publishing the app. When publishing the app with a release build (<code>dotnet publish -c Release</code>), the linker removes any unnecessary code and the download size is much more reasonable (~2MB for the default template).</p>
<p>If you prefer to still run the .NET IL linker on each build during development, you can turn it on by adding <code><BlazorWebAssemblyEnableLinking>true<BlazorWebAssemblyEnableLinking></code> to your project file.</p>
<h2>Build Progressive Web Apps with Blazor</h2>
<p>A Progressive Web App (PWA) is a web-based app that uses modern browser APIs and capabilities to behave like a native app. These capabilities can include:</p>
<ul>
<li>Working offline and always loading instantly, independently of network speed</li>
<li>Being able to run in its own app window, not just a browser window</li>
<li>Being launched from the host operating system (OS) start menu, dock, or home screen</li>
<li>Receiving push notifications from a backend server, even while the user is not using the app</li>
<li>Automatically updating in the background</li>
</ul>
<p>A user might first discover and use the app within their web browser like any other single-page app (SPA), then later progress to installing it in their OS and enabling push notifications.</p>
<p>Blazor WebAssembly is a true standards-based client-side web app platform, so it can use any browser API, including the APIs needed for PWA functionality.</p>
<h3>Using the PWA template</h3>
<p>When creating a new Blazor WebAssembly app, you’re offered the option to add PWA features. In Visual Studio, the option is given as a checkbox in the project creation dialog:</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-6.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-6.png" alt="image"></a></p>
<p>If you’re creating the project on the command line, you can use the <code>--pwa</code> flag. For example,</p>
<pre><code>dotnet new blazorwasm --pwa -o MyNewProject
</code></pre>
<p>In both cases, you’re free to also use the “ASP.NET Core hosted” option if you wish, but don’t have to do so. PWA features are independent of how the app is hosted.</p>
<h3>Installation and app manifest</h3>
<p>When visiting an app created using the PWA template option, users have the option to install the app into their OS’s start menu, dock, or home screen.</p>
<p>The way this option is presented depends on the user’s browser. For example, when using desktop Chromium-based browsers such as Edge or Chrome, an <em>Add</em> button appears within the address bar:</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-7.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-7.png" alt="image"></a></p>
<p>On iOS, visitors can install the PWA using Safari’s <em>Share</em> button and its <em>Add to Homescreen</em> option. On Chrome for Android, users should tap the <em>Menu</em> button in the upper-right corner, then choose <em>Add to Home screen</em>.</p>
<p>Once installed, the app appears in its own window, without any address bar.</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-8.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-8.png" alt="image"></a></p>
<p>To customize the window’s title, color scheme, icon, or other details, see the file <code>manifest.json</code> in your project’s <code>wwwroot</code> directory. The schema of this file is defined by web standards. For detailed documentation, see https://developer.mozilla.org/en-US/docs...nifest.</p>
<h3>Offline support</h3>
<p>By default, apps created using the PWA template option have support for running offline. A user must first visit the app while they are online, then the browser will automatically download and cache all the resources needed to operate offline.</p>
<blockquote>
<p><strong>Important:</strong> Offline support is only enabled for <em>published</em> apps. It is not enabled during development. This is because it would interfere with the usual development cycle of making changes and testing them.</p>
<p><strong>Warning:</strong> If you intend to ship an offline-enabled PWA, there are <a href="https://aka.ms/blazor-offline-considerations">several important warnings and caveats</a> you need to understand. These are inherent to offline PWAs, and not specific to Blazor. Be sure to read and understand these caveats before making assumptions about how your offline-enabled app will work.</p>
</blockquote>
<p>To see how offline support works, first <a href="https://docs.microsoft.com/aspnet/core/host-and-deploy/blazor/#publish-the-app">publish your app</a>, and host it on a server supporting HTTPS. When you visit the app, you should be able to open the browser’s dev tools and verify that a <em>Service Worker</em> is registered for your host:</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-9.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-9.png" alt="image"></a></p>
<p>Additionally, if you reload the page, then on the <em>Network</em> tab you should see that all resources needed to load your page are being retrieved from the <em>Service Worker</em> or <em>Memory Cache</em>:</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-10.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-10.png" alt="image"></a></p>
<p>This shows that the browser is not dependent on network access to load your app. To verify this, you can either shut down your web server, or instruct the browser to simulate offline mode:</p>
<p><a href="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-11.png"> <img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available-11.png" alt="image"></a></p>
<p>Now, even without access to your web server, you should be able to reload the page and see that your app still loads and runs. Likewise, even if you simulate a very slow network connection, your page will still load almost immediately since it’s loaded independently of the network.</p>
<p>To learn more about building PWAs with Blazor, check out the <a href="https://docs.microsoft.com/aspnet/core/blazor/progressive-web-app/">documentation</a>.</p>
<h2>Known issues</h2>
<p>There are a few known issues with this release that you may run into:</p>
<ul>
<li>
<p>When building a Blazor WebAssembly app using an older .NET Core SDK you may see the following build error:</p>
<pre><code>error MSB4018: The "ResolveBlazorRuntimeDependencies" task failed unexpectedly.
error MSB4018: System.IO.FileNotFoundException: Could not load file or assembly '\BlazorApp1\obj\Debug\netstandard2.1\BlazorApp1.dll'. The system cannot find the file specified.
error MSB4018: File name: '\BlazorApp1\obj\Debug\netstandard2.1\BlazorApp1.dll' error MSB4018: at System.Reflection.AssemblyName.nGetFileInformation(String s)
error MSB4018: at System.Reflection.AssemblyName.GetAssemblyName(String assemblyFile)
error MSB4018: at Microsoft.AspNetCore.Components.WebAssembly.Build.ResolveBlazorRuntimeDependencies.GetAssemblyName(String assemblyPath)
error MSB4018: at Microsoft.AspNetCore.Components.WebAssembly.Build.ResolveBlazorRuntimeDependencies.ResolveRuntimeDependenciesCore(String entryPoint, IEnumerable`1 applicationDependencies, IEnumerable`1 monoBclAssemblies)
error MSB4018: at Microsoft.AspNetCore.Components.WebAssembly.Build.ResolveBlazorRuntimeDependencies.Execute()
error MSB4018: at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
error MSB4018: at Microsoft.Build.BackEnd.TaskBuilder.ExecuteInstantiatedTask(ITaskExecutionHost taskExecutionHost, TaskLoggingContext taskLoggingContext, TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteTask)
</code></pre>
<p>To address this issue, upgrade to version 3.1.102 or later of the <a href="https://dotnet.microsoft.com/download/dotnet-core/3.1">.NET Core 3.1 SDK</a>.</p>
</li>
<li>
<p>You may see the following warning when building from the command-line:</p>
<pre><code>CSC : warning CS8034: Unable to load Analyzer assembly C:\Users\user\.nuget\packages\microsoft.aspnetcore.components.analyzers\3.1.0\analyzers\dotnet\cs\Microsoft.AspNetCore.Components.Analyzers.dll : Assembly with same name is already loaded
</code></pre>
<p>This issue will be fixed in a future update to the .NET Core SDK. To workaround this issue, add the <code><DisableImplicitComponentsAnalyzers>true</DisableImplicitComponentsAnalyzers></code> property to the project file.</p>
</li>
</ul>
<h2>Feedback</h2>
<p>We hope you enjoy the new features in this preview release of Blazor WebAssembly! Please let us know what you think by filing issues on <a href="https://github.com/dotnet/aspnetcore/issues">GitHub</a>.</p>
<p>Thanks for trying out Blazor!</p>
<div class="authorinfoarea">
<div><img src="https://www.sickgaming.net/blog/wp-content/uploads/2020/03/blazor-webassembly-3-2-0-preview-2-release-now-available.jpg" width="96" height="96" alt="Daniel Roth" class="avatar avatar-96 wp-user-avatar wp-user-avatar-96 alignnone photo"></div>
<div>
<h5><a class="no-underline" aria-label="Daniel Roth is the name of the author of this post. Click to access author page." href="https://devblogs.microsoft.com/aspnet/author/danroth27/">Daniel Roth</a></h5>
<p>Principal Program Manager, ASP.NET</p>
<p><strong>Follow Daniel</strong> <a class="no-underline stayinformed" aria-label="Daniel Roth Twitter profile" target="_blank" href="https://twitter.com/danroth27" rel="noopener noreferrer"><i class="fa fa-twitter"></i></a><a class="no-underline stayinformed" aria-label="Daniel Roth GitHub profile" target="_blank" href="https://github.com/danroth27" rel="noopener noreferrer"><i class="fa fa-github"></i></a><a class="no-underline stayinformed hvr-pop" aria-label="Daniel Roth RSS Feed" target="_blank" href="https://devblogs.microsoft.com/aspnet/author/danroth27/feed/" rel="noopener noreferrer"></a></p>
</p></div>
</p></div>
</div>
https://www.sickgaming.net/blog/2020/03/...available/