07-25-2019, 08:29 AM
ASP.NET Core and Blazor updates in .NET Core 3.0 Preview 6
<div style="margin: 5px 5% 10px 5%;"><img src="https://www.sickgaming.net/blog/wp-content/uploads/2019/06/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6.jpg" width="150" height="150" title="" alt="" /></div><div><div class="row justify-content-center">
<div class="col-md-4">
<div><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/06/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6.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>June 12th, 2019</p>
</p></div>
<p><!-- .entry-meta --> </p>
<p><a href="https://devblogs.microsoft.com/dotnet/announcing-net-core-3-0-preview-6/">.NET Core 3.0 Preview 6 is now available</a> and it includes a bunch of new updates to ASP.NET Core and Blazor.</p>
<p>Here’s the list of what’s new in this preview:</p>
<ul>
<li>New Razor features: <code>@attribute</code>, <code>@code</code>, <code>@key</code>, <code>@namespace</code>, markup in <code>@functions</code></li>
<li>Blazor directive attributes</li>
<li>Authentication & authorization support for Blazor apps</li>
<li>Static assets in Razor class libraries</li>
<li>Json.NET no longer referenced in project templates</li>
<li>Certificate and Kerberos Authentication</li>
<li>SignalR Auto-reconnect</li>
<li>Managed gRPC Client</li>
<li>gRPC Client Factory</li>
<li>gRPC Interceptors</li>
</ul>
<p>Please see the <a href="https://aka.ms/netcore3releasenotes">release notes</a> for additional details and known issues.</p>
<h2>Get started</h2>
<p>To get started with ASP.NET Core in .NET Core 3.0 Preview 6 <a href="https://aka.ms/netcore3download">install the .NET Core 3.0 Preview 6 SDK</a></p>
<p>If you’re on Windows using Visual Studio, you also need to <a href="https://visualstudio.com/preview">install the latest preview of Visual Studio 2019</a>.</p>
<p>For the latest client-side Blazor templates also install the latest <a href="https://go.microsoft.com/fwlink/?linkid=870389">Blazor extension</a> from the Visual Studio Marketplace.</p>
<h2>Upgrade an existing project</h2>
<p>To upgrade an existing an ASP.NET Core app to .NET Core 3.0 Preview 6, follow the <a href="https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30">migrations steps in the ASP.NET Core docs</a>.</p>
<p>Please also see the full list of <a href="https://github.com/aspnet/announcements/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A3.0.0+label%3A%22Breaking+change%22">breaking changes</a> in ASP.NET Core 3.0.</p>
<p>To upgrade an existing ASP.NET Core 3.0 Preview 5 project to Preview 6:</p>
<ul>
<li>Update Microsoft.AspNetCore.* package references to 3.0.0-preview6.19307.2</li>
<li>In Blazor apps:
<ul>
<li>Rename <code>@functions</code> to <code>@code</code></li>
<li>Update Blazor specific attributes and event handlers to use the new directive attribute syntax (see below)</li>
<li>Remove any call to <code>app.UseBlazor<TStartup>()</code> and instead add a call to <code>app.UseClientSideBlazorFiles<TStartup>()</code> before the call to <code>app.UseRouting()</code>. Also add a call to <code>endpoints.MapFallbackToClientSideBlazor<TStartup>("index.html")</code> in the call to <code>app.UseEndpoints()</code>.</li>
</ul>
</li>
</ul>
<p><em>Before</em></p>
<pre><code class="csharp">app.UseRouting(); app.UseEndpoints(endpoints =>
{ endpoints.MapDefaultControllerRoute();
}); app.UseBlazor<Client.Startup>();
</code></pre>
<p><em>After</em></p>
<pre><code class="csharp">app.UseClientSideBlazorFiles<Client.Startup>(); app.UseRouting(); app.UseEndpoints(endpoints =>
{ endpoints.MapDefaultControllerRoute(); endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
});
</code></pre>
<h2>New Razor features</h2>
<p>We’ve added support for the following new Razor language features in this release.</p>
<h3><code>@attribute</code></h3>
<p>The new <code>@attribute</code> directive adds the specified attribute to the generated class.</p>
<pre><code class="csharp">@attribute [Authorize]
</code></pre>
<h3><code>@code</code></h3>
<p>The new <code>@code</code> directive is used in .razor files (not supported in .cshtml files) to specify a code block to add to the generated class as additional members. It’s equivalent to <code>@functions</code>, but now with a better name.</p>
<pre><code class="csharp">@code { int currentCount = 0; void IncrementCount() { currentCount++; }
}
</code></pre>
<h3><code>@key</code></h3>
<p>The new <code>@key</code> directive attribute is used in .razor files to specify a value (any object or unique identifier) that the Blazor diffing algorithm can use to preserve elements or components in a list.</p>
<pre><code class="html"><div> @foreach (var flight in Flights) { }
</div>
</code></pre>
<p>To understand why this feature is needed, consider rendering a list of cards with flight details without this feature:</p>
<pre><code class="html"><div> @foreach (var flight in Flights) { }
</div>
</code></pre>
<p>If you add a new flight into the middle of the <code>Flights</code> list the existing <code>DetailsCard</code> instances should remain unaffected and one new <code>DetailsCard</code> should be inserted into the rendered output.</p>
<p>To visualize this, if <code>Flights</code> previously contained <code>[F0, F1, F2]</code>, then this is the <strong>before</strong> state:</p>
<ul>
<li>DetailsCard0, with Flight=F0</li>
<li>DetailsCard1, with Flight=F1</li>
<li>DetailsCard2, with Flight=F2</li>
</ul>
<p>… and this is the <strong>desired after</strong> state, given we insert a new item <code>FNew</code> at index 1:</p>
<ul>
<li>DetailsCard0, with Flight=F0</li>
<li>DetailsCardNew, with Flight=FNew</li>
<li>DetailsCard1, with Flight=F1</li>
<li>DetailsCard2, with Flight=F2</li>
</ul>
<p>However, the <strong>actual after</strong> state this:</p>
<ul>
<li>DetailsCard0, with Flight=F0</li>
<li>DetailsCard1, with Flight=FNew</li>
<li>DetailsCard2, with Flight=F1</li>
<li>DetailsCardNew, with Flight=F2</li>
</ul>
<p>The system has no way to know that DetailsCard2 or DetailsCard3 should preserve their associations with their older Flight instances, so it just re-associates them with whatever Flight matches their position in the list. As a result, DetailsCard1 and DetailsCard2 rebuild themselves completely using new data, which is wasteful and sometimes even leads to user-visible problems (e.g., input focus is unexpectedly lost).</p>
<p>By adding keys using <code>@key</code> the diffing algorithm can associate the old and new elements or components.</p>
<h3><code>@namespace</code></h3>
<p>Specifies the namespace for the generated class or the namespace prefix when used in an <em>_Imports.razor</em> file. The <code>@namespace</code> directive works today in pages and views (.cshtml) apps, but is now it is also supported with components (.razor).</p>
<pre><code class="csharp">@namespace MyNamespace
</code></pre>
<h3>Markup in <code>@functions</code> and local functions</h3>
<p>In views and pages (.cshtml files) you can now add markup inside of methods in the <code>@functions</code> block and in local functions.</p>
<pre><code>@{ GreetPerson(person); } @functions { void GreetPerson(Person person) { <p>Hello, <em>@person.Name!</em></p> }
}
</code></pre>
<h2>Blazor directive attributes</h2>
<p>Blazor uses a variety of attributes for influencing how components get compiled (e.g. ref, bind, event handlers, etc.). These attributes have been added organically to Blazor over time and use different syntaxes. In this Blazor release we’ve standardized on a common syntax for directive attributes. This makes the Razor syntax used by Blazor more consistent and predictable. It also paves the way for future extensibility.</p>
<p>Directive attributes all follow the following syntax where the values in parenthesis are optional:</p>
<pre><code>@directive(-suffix(:name))(="value")
</code></pre>
<p>Some valid examples:</p>
<pre><code class="html"><!-- directive -->
<div>...</div>
<div></div> <!-- directive with key/value arg-->
<div>...</div>
<div></div> <!-- directive with suffix -->
<div></div>
<div></div> <!-- directive with suffix and key/value arg-->
<div></div>
<div></div>
</code></pre>
<p>All of the Blazor built-in directive attributes have been updated to use this new syntax as described below.</p>
<p><strong>Event handlers</strong></p>
<p>Specifying event handlers in Blazor now uses the new directive attribute syntax instead of the normal HTML syntax. The syntax is similar to the HTML syntax, but now with a leading <code>@</code> character. This makes C# event handlers distinct from JS event handlers.</p>
<pre><code class="html"><button @onclick="@Clicked">Click me!</button>
</code></pre>
<p>When specifying a delegate for C# event handler the <code>@</code> prefix is currently still required on the attribute value, but we expect to remove this requirement in a future update.</p>
<p>In the future we also expect to use the directive attribute syntax to support additional features for event handlers. For example, stopping event propagation will likely look something like this (not implemented yet, but it gives you an idea of scenarios now enabled by directive attributes):</p>
<pre><code class="html"><button @onclick="Clicked" @onclick:stopPropagation>Click me!</button>
</code></pre>
<p><strong>Bind</strong></p>
<pre><code class="html"><input @bind="myValue">...</input>
<input @bind="myValue" @bind:format="mm/dd">...</input>
<MyButton @bind-Value="myValue">...</MyButton>
</code></pre>
<p><strong>Key</strong></p>
<pre><code class="html"><div>...</div>
</code></pre>
<p><strong>Ref</strong></p>
<pre><code class="html"><button @ref="myButton">...</button>
</code></pre>
<h2>Authentication & authorization support for Blazor apps</h2>
<p>Blazor now has built-in support for handling authentication and authorization. The server-side Blazor template now supports options for enabling all of the standard authentication configurations using ASP.NET Core Identity, Azure AD, and Azure AD B2C. We haven’t updated the Blazor WebAssembly templates to support these options yet, but we plan to do so after .NET Core 3.0 has shipped.</p>
<p>To create a new Blazor app with authentication enabled:</p>
<ol>
<li>
<p>Create a new Blazor (server-side) project and select the link to change the authentication configuration. For example, select “Individual User Accounts” and “Store user accounts in-app” to use Blazor with ASP.NET Core Identity:</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/06/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6.png" alt="Blazor authentication"></p>
</li>
<li>
<p>Run the app. The app includes links in the top row for registering as a new user and logging in.</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/06/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6-1.png" alt="Blazor authentication running"></p>
</li>
<li>
<p>Select the Register link to register a new user.</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/06/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6-2.png" alt="Blazor authentication register"></p>
</li>
<li>
<p>Select “Apply Migrations” to apply the ASP.NET Core Identity migrations to the database.</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/06/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6-3.png" alt="Blazor authentication apply migrations"></p>
</li>
<li>
<p>You should now be logged in.</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/06/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6-4.png" alt="Blazor authentication logged in"></p>
</li>
<li>
<p>Select your user name to edit your user profile.</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/06/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6-5.png" alt="Blazor authentication edit profile"></p>
</li>
</ol>
<p>In the Blazor app, authentication and authorization are configured in the <code>Startup</code> class using the standard ASP.NET Core middleware.</p>
<pre><code class="csharp">app.UseRouting(); app.UseAuthentication();
app.UseAuthorization(); app.UseEndpoints(endpoints =>
{ endpoints.MapControllers(); endpoints.MapBlazorHub(); endpoints.MapFallbackToPage("/_Host");
});
</code></pre>
<p>When using ASP.NET Core Identity all of the identity related UI concerns are handled by the framework provided default identity UI.</p>
<pre><code class="csharp">services.AddDefaultIdentity<IdentityUser>() .AddEntityFrameworkStores<ApplicationDbContext>();
</code></pre>
<p>The authentication related links in top row of the app are rendered using the new built-in <code>AuthorizeView</code> component, which displays different content depending on the authentication state.</p>
<p><em>LoginDisplay.razor</em></p>
<pre><code class="html"><AuthorizeView> <Authorized> <a href="Identity/Account/Manage">Hello, @context.User.Identity.Name!</a> <a href="Identity/Account/LogOut">Log out</a> </Authorized> <NotAuthorized> <a href="Identity/Account/Register">Register</a> <a href="Identity/Account/Login">Log in</a> </NotAuthorized>
</AuthorizeView>
</code></pre>
<p>The <code>AuthorizeView</code> component will only display its child content when the user is authorized. Alternatively, the <code>AuthorizeView</code> takes parameters for specifying different templates when the user is <code>Authorized</code>, <code>NotAuthorized</code>, or <code>Authorizing</code>. The current authentication state is passed to these templates through the implicit <code>context</code> parameter. You can also specify specific roles or an authorization policy on the <code>AuthorizeView</code> that the user must satisfy to see the authorized view.</p>
<p>To authorize access to specific pages in a Blazor app, use the normal <code>[Authorize]</code> attribute. You can apply the <code>[Authorize]</code> attribute to a component using the new <code>@attribute</code> directive.</p>
<pre><code class="csharp">@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@page "/fetchdata"
</code></pre>
<p>To specify what content to display on a page that requires authorization when the user isn’t authorized or is still in the processing of authorizing, use the <code>NotAuthorizedContent</code> and <code>AuthorizingContent</code> parameters on the <code>Router</code> component. These <code>Router</code> parameters are only support in client-side Blazor for this release, but they will be enabled for server-side Blazor in a future update.</p>
<p>The new <code>AuthenticationStateProvider</code> service make the authentication state available to Blazor apps in a uniform way whether they run on the server or client-side in the browser. In server-side Blazor apps the <code>AuthenticationStateProvider</code> surfaces the user from the <code>HttpContext</code> that established the connection to the server. Client-side Blazor apps can configure a custom <code>AuthenticationStateProvider</code> as appropriate for that application. For example, it might retrieve the current user information by querying an endpoint on the server.</p>
<p>The authentication state is made available to the app as a cascading value (<code>Task<AuthenticationState></code>) using the <code>CascadingAuthenticationState</code> component. This cascading value is then used by the <code>AuthorizeView</code> and <code>Router</code> components to authorize access to specific parts of the UI.</p>
<p><em>App.razor</em></p>
<pre><code class="html"><CascadingAuthenticationState> <Router AppAssembly="typeof(Startup).Assembly"> <NotFoundContent> <p>Sorry, there's nothing at this address.</p> </NotFoundContent> </Router>
</CascadingAuthenticationState>
</code></pre>
<h2>Static assets in Razor class libraries</h2>
<p>Razor class libraries can now include static assets like JavaScript, CSS, and images. These static assets can then be included in ASP.NET Core apps by referencing the Razor class library project or via a package reference.</p>
<p>To include static assets in a Razor class library add a <em>wwwroot</em> folder to the Razor class library and include any required files in that folder.</p>
<p>When a Razor class library with static assets is referenced either as a project reference or as a package, the static assets from the library are made available to the app under the path prefix <em>_content/{LIBRARY NAME}/</em>. The static assets stay in their original folders and any changes to the content of static assets in the Razor class libraries are reflected in the app without rebuilding.</p>
<p>When the app is published, the companion assets from all referenced Razor class libraries are copied into the <em>wwwroot</em> folder of the published app under the same prefix.</p>
<p>To try out using static assets from a Razor class library:</p>
<ol>
<li>
<p>Create a default ASP.NET Core Web App.</p>
<pre><code class="console">dotnet new webapp -o WebApp1
</code></pre>
</li>
<li>
<p>Create a Razor class library and reference it from the web app.</p>
<pre><code class="console">dotnet new razorclasslib -o RazorLib1
dotnet add WebApp1 reference RazorLib1
</code></pre>
</li>
<li>
<p>Add a <em>wwwroot</em> folder to the Razor class library and include a JavaScript file that logs a simple message to the console.</p>
<pre><code class="console">cd RazorLib1
mkdir wwwroot
</code></pre>
<p><em>hello.js</em></p>
<pre><code class="js">console.log("Hello from RazorLib1!");
</code></pre>
</li>
<li>
<p>Reference the script file from <em>Index.cshtml</em> in the web app.</p>
<pre><code class="html"><a href="http://_content/RazorLib1/hello.js">http://_content/RazorLib1/hello.js</a>
</code></pre>
</li>
<li>
<p>Run the app and look for the output in the browser console.</p>
<pre><code>Hello from RazorLib1!
</code></pre>
</li>
</ol>
<h2>Projects now use System.Text.Json by default</h2>
<p>New ASP.NET Core projects will now use System.Text.Json for JSON handling by default. In this release we removed Json.NET (Newtonsoft.Json) from the project templates. To enable support for using Json.NET, add the Microsoft.AspNetCore.Mvc.NewtonsoftJson package to your project and add a call to <code>AddNewtonsoftJson()</code> following code in your <code>Startup.ConfigureServices</code> method. For example:</p>
<pre><code class="csharp">services.AddMvc() .AddNewtonsoftJson();
</code></pre>
<h2>Certificate and Kerberos authentication</h2>
<p>Preview 6 brings Certificate and Kerberos authentication to ASP.NET Core.</p>
<p>Certificate authentication requires you to configure your server to accept certificates, and then add the authentication middleware in <code>Startup.Configure</code> and the certificate authentication service in <code>Startup.ConfigureServices</code>.</p>
<pre><code class="csharp">public void ConfigureServices(IServiceCollection services)
{ services.AddAuthentication( CertificateAuthenticationDefaults.AuthenticationScheme) .AddCertificate(); // All the other service configuration.
} public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{ app.UseAuthentication(); // All the other app configuration.
}
</code></pre>
<p>Options for certificate authentication include the ability to accept self-signed certificates, check for certificate revocation, and check that the proffered certificate has the right usage flags in it. A default user principal is constructed from the certificate properties, with an event that enables you to supplement or replace the principal. All the options, and instructions on how to configure common hosts for certificate authentication can be found in the <a href="https://docs.microsoft.com/en-us/aspnet/core/security/authentication/certauth?view=aspnetcore-3.0">documentation</a>.</p>
<p>We’ve also extended “Windows Authentication” onto Linux and macOS. Previously this authentication type was limited to IIS and HttpSys, but now Kestrel has the ability to use Negotiate, Kerberos, and NTLM on Windows, Linux, and macOS for Windows domain joined hosts by using the Microsoft.AspNetCore.Authentication.Negotiate nuget package. As with the other authentication services you configure authentication app wide, then configure the service:</p>
<pre><code class="csharp">public void ConfigureServices(IServiceCollection services)
{ services.AddAuthentication(NegotiateDefaults.AuthenticationScheme) .AddNegotiate();
} public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{ app.UseAuthentication(); // All the other app configuration.
}
</code></pre>
<p>Your host must be configured correctly. Windows hosts must have SPNs added to the user account hosting the application. Linux and macOS machines must be joined to the domain, then SPNs must be created for the web process, as well as <a href="https://blogs.technet.microsoft.com/pie/2018/01/03/all-you-need-to-know-about-keytab-files/">keytab</a> files generated and configured on the host machine. Full instructions are given in the <a href="https://docs.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.0">documentation</a>.</p>
<h2>SignalR Auto-reconnect</h2>
<p>This preview release, available now via <code>npm install @aspnet/signalr@next</code> and in the .NET Core SignalR Client, includes a new automatic reconnection feature. With this release we’ve added the <code>withAutomaticReconnect()</code> method to the <code>HubConnectionBuilder</code>. By default, the client will try to reconnect immediately and after 2, 10, and 30 seconds. Enlisting in automatic reconnect is opt-in, but simple via this new method.</p>
<pre><code class="javascript">const connection = new signalR.HubConnectionBuilder() .withUrl("/chatHub") .withAutomaticReconnect() .build();
</code></pre>
<p>By passing an array of millisecond-based durations to the method, you can be very granular about how your reconnection attempts occur over time.</p>
<pre><code class="javascript">.withAutomaticReconnect([0, 3000, 5000, 10000, 15000, 30000])
//.withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior
</code></pre>
<p>Or you can pass in an implementation of a custom reconnect policy that gives you full control.</p>
<p>If the reconnection fails after the 30-second point (or whatever you’ve set as your maximum), the client presumes the connection is offline and stops trying to reconnect. During these reconnection attempts you’ll want to update your application UI to provide cues to the user that the reconnection is being attempted.</p>
<h3>Reconnection Event Handlers</h3>
<p>To make this easier, we’ve expanded the SignalR client API to include <code>onreconnecting</code> and <code>onreconnected</code> event handlers. The first of these handlers, <code>onreconnecting</code>, gives developers a good opportunity to disable UI or to let users know the app is offline.</p>
<pre><code class="javascript">connection.onreconnecting((error) => { const status = `Connection lost due to error "${error}". Reconnecting.`; document.getElementById("messageInput").disabled = true; document.getElementById("sendButton").disabled = true; document.getElementById("connectionStatus").innerText = status;
});
</code></pre>
<p>Likewise, the <code>onreconnected</code> handler gives developers an opportunity to update the UI once the connection is reestablished.</p>
<pre><code class="javascript">connection.onreconnected((connectionId) => { const status = `Connection reestablished. Connected.`; document.getElementById("messageInput").disabled = false; document.getElementById("sendButton").disabled = false; document.getElementById("connectionStatus").innerText = status;
});
</code></pre>
<h3>Learn more about customizing and handling reconnection</h3>
<p>Automatic reconnect has been partially documented already in the preview release. Check out the deeper docs on the topic, with more examples and details on usage, at <a href="https://aka.ms/signalr/auto-reconnect">https://aka.ms/signalr/auto-reconnect</a>.</p>
<h2>Managed gRPC Client</h2>
<p>In prior previews, we relied on the <code>Grpc.Core</code> library for client support. The addition of HTTP/2 support in <code>HttpClient</code> in this preview has allowed us to introduce a fully managed gRPC client.</p>
<p>To begin using the new client, add a package reference to <code>Grpc.Net.Client</code> and then you can create a new client.</p>
<pre><code class="csharp">var httpClient = new HttpClient() { BaseAddress = new Uri("https://localhost:5001") };
var client = GrpcClient.Create<GreeterClient>(httpClient);
</code></pre>
<h2>gRPC Client Factory</h2>
<p>Building on the opinionated pattern we introduced in <code>HttpClientFactory</code>, we’ve added a gRPC client factory for creating gRPC client instances in your project. There are two flavors of the factory that we’ve added: <code>Grpc.Net.ClientFactory</code> and <code>Grpc.AspNetCore.Server.ClientFactory</code>.</p>
<p>The <code>Grpc.Net.ClientFactory</code> is designed for use in non-ASP.NET app models (such as Worker Services) that still use the <code>Microsoft.Extensions.*</code> primitives without a dependency on ASP.NET Core.</p>
<p>In applications that perform service-to-service communication, we often observe that most servers are also clients that consume other services. In these scenarios, we recommend the use of <code>Grpc.AspNetCore.Server.ClientFactory</code> which features automatic propagation of gRPC deadlines and cancellation tokens.</p>
<p>To use the client factory, add the appropriate package reference to your project (<code>Grpc.AspNetCore.Server.Factory</code> or <code>Grpc.Net.ClientFactory</code>) before adding the following code to <code>ConfigureServices()</code>.</p>
<pre><code class="csharp">services .AddGrpcClient<GreeterClient>(options => { options.BaseAddress = new Uri("https://localhost:5001"); });
</code></pre>
<h2>gRPC Interceptors</h2>
<p>gRPC exposes a mechanism to intercept RPC invocations on both the client and the server. Interceptors can be used in conjunction with existing HTTP middleware. Unlike HTTP middleware, interceptors give you access to actual request/response objects before serialization (on the client) and after deserialization (on the server) and vice versa for the response. All middlewares run before interceptors on the request side and vice versa on the response side.</p>
<h3>Client interceptors</h3>
<p>When used in conjunction with the client factory, you can add a client interceptor as shown below.</p>
<pre><code class="csharp">services .AddGrpcClient<GreeterClient>(options => { options.BaseAddress = new Uri("https://localhost:5001"); }) .AddInterceptor<CallbackInterceptor>();
</code></pre>
<h3>Server interceptors</h3>
<p>Server interceptors can be registered in <code>ConfigureServices()</code> as shown below.</p>
<pre><code class="csharp">services .AddGrpc(options => { // This registers a global interceptor options.Interceptors.Add<MaxStreamingRequestTimeoutInterceptor>(TimeSpan.FromSeconds(30)); }) .AddServiceOptions<GreeterService>(options => { // This registers an interceptor for the Greeter service options.Interceptors.Add<UnaryCachingInterceptor>(); });
</code></pre>
<p>For examples on how to author an interceptors, take a look at <a href="https://github.com/grpc/grpc-dotnet/tree/master/examples/Server/Interceptors">these examples</a> in the grpc-dotnet repo.</p>
<h2>Give feedback</h2>
<p>We hope you enjoy the new features in this preview release of ASP.NET Core and Blazor! Please let us know what you think by filing issues on <a href="https://github.com/aspnet/aspnetcore/issues">GitHub</a>.</p>
<p>Thanks for trying out ASP.NET Core and Blazor!</p>
<div class="authorinfoarea">
<div><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/06/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6.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" target="_blank" href="https://devblogs.microsoft.com/aspnet/author/danroth27/" rel="noopener noreferrer">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>
<div style="margin: 5px 5% 10px 5%;"><img src="https://www.sickgaming.net/blog/wp-content/uploads/2019/06/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6.jpg" width="150" height="150" title="" alt="" /></div><div><div class="row justify-content-center">
<div class="col-md-4">
<div><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/06/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6.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>June 12th, 2019</p>
</p></div>
<p><!-- .entry-meta --> </p>
<p><a href="https://devblogs.microsoft.com/dotnet/announcing-net-core-3-0-preview-6/">.NET Core 3.0 Preview 6 is now available</a> and it includes a bunch of new updates to ASP.NET Core and Blazor.</p>
<p>Here’s the list of what’s new in this preview:</p>
<ul>
<li>New Razor features: <code>@attribute</code>, <code>@code</code>, <code>@key</code>, <code>@namespace</code>, markup in <code>@functions</code></li>
<li>Blazor directive attributes</li>
<li>Authentication & authorization support for Blazor apps</li>
<li>Static assets in Razor class libraries</li>
<li>Json.NET no longer referenced in project templates</li>
<li>Certificate and Kerberos Authentication</li>
<li>SignalR Auto-reconnect</li>
<li>Managed gRPC Client</li>
<li>gRPC Client Factory</li>
<li>gRPC Interceptors</li>
</ul>
<p>Please see the <a href="https://aka.ms/netcore3releasenotes">release notes</a> for additional details and known issues.</p>
<h2>Get started</h2>
<p>To get started with ASP.NET Core in .NET Core 3.0 Preview 6 <a href="https://aka.ms/netcore3download">install the .NET Core 3.0 Preview 6 SDK</a></p>
<p>If you’re on Windows using Visual Studio, you also need to <a href="https://visualstudio.com/preview">install the latest preview of Visual Studio 2019</a>.</p>
<p>For the latest client-side Blazor templates also install the latest <a href="https://go.microsoft.com/fwlink/?linkid=870389">Blazor extension</a> from the Visual Studio Marketplace.</p>
<h2>Upgrade an existing project</h2>
<p>To upgrade an existing an ASP.NET Core app to .NET Core 3.0 Preview 6, follow the <a href="https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30">migrations steps in the ASP.NET Core docs</a>.</p>
<p>Please also see the full list of <a href="https://github.com/aspnet/announcements/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A3.0.0+label%3A%22Breaking+change%22">breaking changes</a> in ASP.NET Core 3.0.</p>
<p>To upgrade an existing ASP.NET Core 3.0 Preview 5 project to Preview 6:</p>
<ul>
<li>Update Microsoft.AspNetCore.* package references to 3.0.0-preview6.19307.2</li>
<li>In Blazor apps:
<ul>
<li>Rename <code>@functions</code> to <code>@code</code></li>
<li>Update Blazor specific attributes and event handlers to use the new directive attribute syntax (see below)</li>
<li>Remove any call to <code>app.UseBlazor<TStartup>()</code> and instead add a call to <code>app.UseClientSideBlazorFiles<TStartup>()</code> before the call to <code>app.UseRouting()</code>. Also add a call to <code>endpoints.MapFallbackToClientSideBlazor<TStartup>("index.html")</code> in the call to <code>app.UseEndpoints()</code>.</li>
</ul>
</li>
</ul>
<p><em>Before</em></p>
<pre><code class="csharp">app.UseRouting(); app.UseEndpoints(endpoints =>
{ endpoints.MapDefaultControllerRoute();
}); app.UseBlazor<Client.Startup>();
</code></pre>
<p><em>After</em></p>
<pre><code class="csharp">app.UseClientSideBlazorFiles<Client.Startup>(); app.UseRouting(); app.UseEndpoints(endpoints =>
{ endpoints.MapDefaultControllerRoute(); endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
});
</code></pre>
<h2>New Razor features</h2>
<p>We’ve added support for the following new Razor language features in this release.</p>
<h3><code>@attribute</code></h3>
<p>The new <code>@attribute</code> directive adds the specified attribute to the generated class.</p>
<pre><code class="csharp">@attribute [Authorize]
</code></pre>
<h3><code>@code</code></h3>
<p>The new <code>@code</code> directive is used in .razor files (not supported in .cshtml files) to specify a code block to add to the generated class as additional members. It’s equivalent to <code>@functions</code>, but now with a better name.</p>
<pre><code class="csharp">@code { int currentCount = 0; void IncrementCount() { currentCount++; }
}
</code></pre>
<h3><code>@key</code></h3>
<p>The new <code>@key</code> directive attribute is used in .razor files to specify a value (any object or unique identifier) that the Blazor diffing algorithm can use to preserve elements or components in a list.</p>
<pre><code class="html"><div> @foreach (var flight in Flights) { }
</div>
</code></pre>
<p>To understand why this feature is needed, consider rendering a list of cards with flight details without this feature:</p>
<pre><code class="html"><div> @foreach (var flight in Flights) { }
</div>
</code></pre>
<p>If you add a new flight into the middle of the <code>Flights</code> list the existing <code>DetailsCard</code> instances should remain unaffected and one new <code>DetailsCard</code> should be inserted into the rendered output.</p>
<p>To visualize this, if <code>Flights</code> previously contained <code>[F0, F1, F2]</code>, then this is the <strong>before</strong> state:</p>
<ul>
<li>DetailsCard0, with Flight=F0</li>
<li>DetailsCard1, with Flight=F1</li>
<li>DetailsCard2, with Flight=F2</li>
</ul>
<p>… and this is the <strong>desired after</strong> state, given we insert a new item <code>FNew</code> at index 1:</p>
<ul>
<li>DetailsCard0, with Flight=F0</li>
<li>DetailsCardNew, with Flight=FNew</li>
<li>DetailsCard1, with Flight=F1</li>
<li>DetailsCard2, with Flight=F2</li>
</ul>
<p>However, the <strong>actual after</strong> state this:</p>
<ul>
<li>DetailsCard0, with Flight=F0</li>
<li>DetailsCard1, with Flight=FNew</li>
<li>DetailsCard2, with Flight=F1</li>
<li>DetailsCardNew, with Flight=F2</li>
</ul>
<p>The system has no way to know that DetailsCard2 or DetailsCard3 should preserve their associations with their older Flight instances, so it just re-associates them with whatever Flight matches their position in the list. As a result, DetailsCard1 and DetailsCard2 rebuild themselves completely using new data, which is wasteful and sometimes even leads to user-visible problems (e.g., input focus is unexpectedly lost).</p>
<p>By adding keys using <code>@key</code> the diffing algorithm can associate the old and new elements or components.</p>
<h3><code>@namespace</code></h3>
<p>Specifies the namespace for the generated class or the namespace prefix when used in an <em>_Imports.razor</em> file. The <code>@namespace</code> directive works today in pages and views (.cshtml) apps, but is now it is also supported with components (.razor).</p>
<pre><code class="csharp">@namespace MyNamespace
</code></pre>
<h3>Markup in <code>@functions</code> and local functions</h3>
<p>In views and pages (.cshtml files) you can now add markup inside of methods in the <code>@functions</code> block and in local functions.</p>
<pre><code>@{ GreetPerson(person); } @functions { void GreetPerson(Person person) { <p>Hello, <em>@person.Name!</em></p> }
}
</code></pre>
<h2>Blazor directive attributes</h2>
<p>Blazor uses a variety of attributes for influencing how components get compiled (e.g. ref, bind, event handlers, etc.). These attributes have been added organically to Blazor over time and use different syntaxes. In this Blazor release we’ve standardized on a common syntax for directive attributes. This makes the Razor syntax used by Blazor more consistent and predictable. It also paves the way for future extensibility.</p>
<p>Directive attributes all follow the following syntax where the values in parenthesis are optional:</p>
<pre><code>@directive(-suffix(:name))(="value")
</code></pre>
<p>Some valid examples:</p>
<pre><code class="html"><!-- directive -->
<div>...</div>
<div></div> <!-- directive with key/value arg-->
<div>...</div>
<div></div> <!-- directive with suffix -->
<div></div>
<div></div> <!-- directive with suffix and key/value arg-->
<div></div>
<div></div>
</code></pre>
<p>All of the Blazor built-in directive attributes have been updated to use this new syntax as described below.</p>
<p><strong>Event handlers</strong></p>
<p>Specifying event handlers in Blazor now uses the new directive attribute syntax instead of the normal HTML syntax. The syntax is similar to the HTML syntax, but now with a leading <code>@</code> character. This makes C# event handlers distinct from JS event handlers.</p>
<pre><code class="html"><button @onclick="@Clicked">Click me!</button>
</code></pre>
<p>When specifying a delegate for C# event handler the <code>@</code> prefix is currently still required on the attribute value, but we expect to remove this requirement in a future update.</p>
<p>In the future we also expect to use the directive attribute syntax to support additional features for event handlers. For example, stopping event propagation will likely look something like this (not implemented yet, but it gives you an idea of scenarios now enabled by directive attributes):</p>
<pre><code class="html"><button @onclick="Clicked" @onclick:stopPropagation>Click me!</button>
</code></pre>
<p><strong>Bind</strong></p>
<pre><code class="html"><input @bind="myValue">...</input>
<input @bind="myValue" @bind:format="mm/dd">...</input>
<MyButton @bind-Value="myValue">...</MyButton>
</code></pre>
<p><strong>Key</strong></p>
<pre><code class="html"><div>...</div>
</code></pre>
<p><strong>Ref</strong></p>
<pre><code class="html"><button @ref="myButton">...</button>
</code></pre>
<h2>Authentication & authorization support for Blazor apps</h2>
<p>Blazor now has built-in support for handling authentication and authorization. The server-side Blazor template now supports options for enabling all of the standard authentication configurations using ASP.NET Core Identity, Azure AD, and Azure AD B2C. We haven’t updated the Blazor WebAssembly templates to support these options yet, but we plan to do so after .NET Core 3.0 has shipped.</p>
<p>To create a new Blazor app with authentication enabled:</p>
<ol>
<li>
<p>Create a new Blazor (server-side) project and select the link to change the authentication configuration. For example, select “Individual User Accounts” and “Store user accounts in-app” to use Blazor with ASP.NET Core Identity:</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/06/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6.png" alt="Blazor authentication"></p>
</li>
<li>
<p>Run the app. The app includes links in the top row for registering as a new user and logging in.</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/06/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6-1.png" alt="Blazor authentication running"></p>
</li>
<li>
<p>Select the Register link to register a new user.</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/06/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6-2.png" alt="Blazor authentication register"></p>
</li>
<li>
<p>Select “Apply Migrations” to apply the ASP.NET Core Identity migrations to the database.</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/06/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6-3.png" alt="Blazor authentication apply migrations"></p>
</li>
<li>
<p>You should now be logged in.</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/06/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6-4.png" alt="Blazor authentication logged in"></p>
</li>
<li>
<p>Select your user name to edit your user profile.</p>
<p><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/06/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6-5.png" alt="Blazor authentication edit profile"></p>
</li>
</ol>
<p>In the Blazor app, authentication and authorization are configured in the <code>Startup</code> class using the standard ASP.NET Core middleware.</p>
<pre><code class="csharp">app.UseRouting(); app.UseAuthentication();
app.UseAuthorization(); app.UseEndpoints(endpoints =>
{ endpoints.MapControllers(); endpoints.MapBlazorHub(); endpoints.MapFallbackToPage("/_Host");
});
</code></pre>
<p>When using ASP.NET Core Identity all of the identity related UI concerns are handled by the framework provided default identity UI.</p>
<pre><code class="csharp">services.AddDefaultIdentity<IdentityUser>() .AddEntityFrameworkStores<ApplicationDbContext>();
</code></pre>
<p>The authentication related links in top row of the app are rendered using the new built-in <code>AuthorizeView</code> component, which displays different content depending on the authentication state.</p>
<p><em>LoginDisplay.razor</em></p>
<pre><code class="html"><AuthorizeView> <Authorized> <a href="Identity/Account/Manage">Hello, @context.User.Identity.Name!</a> <a href="Identity/Account/LogOut">Log out</a> </Authorized> <NotAuthorized> <a href="Identity/Account/Register">Register</a> <a href="Identity/Account/Login">Log in</a> </NotAuthorized>
</AuthorizeView>
</code></pre>
<p>The <code>AuthorizeView</code> component will only display its child content when the user is authorized. Alternatively, the <code>AuthorizeView</code> takes parameters for specifying different templates when the user is <code>Authorized</code>, <code>NotAuthorized</code>, or <code>Authorizing</code>. The current authentication state is passed to these templates through the implicit <code>context</code> parameter. You can also specify specific roles or an authorization policy on the <code>AuthorizeView</code> that the user must satisfy to see the authorized view.</p>
<p>To authorize access to specific pages in a Blazor app, use the normal <code>[Authorize]</code> attribute. You can apply the <code>[Authorize]</code> attribute to a component using the new <code>@attribute</code> directive.</p>
<pre><code class="csharp">@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@page "/fetchdata"
</code></pre>
<p>To specify what content to display on a page that requires authorization when the user isn’t authorized or is still in the processing of authorizing, use the <code>NotAuthorizedContent</code> and <code>AuthorizingContent</code> parameters on the <code>Router</code> component. These <code>Router</code> parameters are only support in client-side Blazor for this release, but they will be enabled for server-side Blazor in a future update.</p>
<p>The new <code>AuthenticationStateProvider</code> service make the authentication state available to Blazor apps in a uniform way whether they run on the server or client-side in the browser. In server-side Blazor apps the <code>AuthenticationStateProvider</code> surfaces the user from the <code>HttpContext</code> that established the connection to the server. Client-side Blazor apps can configure a custom <code>AuthenticationStateProvider</code> as appropriate for that application. For example, it might retrieve the current user information by querying an endpoint on the server.</p>
<p>The authentication state is made available to the app as a cascading value (<code>Task<AuthenticationState></code>) using the <code>CascadingAuthenticationState</code> component. This cascading value is then used by the <code>AuthorizeView</code> and <code>Router</code> components to authorize access to specific parts of the UI.</p>
<p><em>App.razor</em></p>
<pre><code class="html"><CascadingAuthenticationState> <Router AppAssembly="typeof(Startup).Assembly"> <NotFoundContent> <p>Sorry, there's nothing at this address.</p> </NotFoundContent> </Router>
</CascadingAuthenticationState>
</code></pre>
<h2>Static assets in Razor class libraries</h2>
<p>Razor class libraries can now include static assets like JavaScript, CSS, and images. These static assets can then be included in ASP.NET Core apps by referencing the Razor class library project or via a package reference.</p>
<p>To include static assets in a Razor class library add a <em>wwwroot</em> folder to the Razor class library and include any required files in that folder.</p>
<p>When a Razor class library with static assets is referenced either as a project reference or as a package, the static assets from the library are made available to the app under the path prefix <em>_content/{LIBRARY NAME}/</em>. The static assets stay in their original folders and any changes to the content of static assets in the Razor class libraries are reflected in the app without rebuilding.</p>
<p>When the app is published, the companion assets from all referenced Razor class libraries are copied into the <em>wwwroot</em> folder of the published app under the same prefix.</p>
<p>To try out using static assets from a Razor class library:</p>
<ol>
<li>
<p>Create a default ASP.NET Core Web App.</p>
<pre><code class="console">dotnet new webapp -o WebApp1
</code></pre>
</li>
<li>
<p>Create a Razor class library and reference it from the web app.</p>
<pre><code class="console">dotnet new razorclasslib -o RazorLib1
dotnet add WebApp1 reference RazorLib1
</code></pre>
</li>
<li>
<p>Add a <em>wwwroot</em> folder to the Razor class library and include a JavaScript file that logs a simple message to the console.</p>
<pre><code class="console">cd RazorLib1
mkdir wwwroot
</code></pre>
<p><em>hello.js</em></p>
<pre><code class="js">console.log("Hello from RazorLib1!");
</code></pre>
</li>
<li>
<p>Reference the script file from <em>Index.cshtml</em> in the web app.</p>
<pre><code class="html"><a href="http://_content/RazorLib1/hello.js">http://_content/RazorLib1/hello.js</a>
</code></pre>
</li>
<li>
<p>Run the app and look for the output in the browser console.</p>
<pre><code>Hello from RazorLib1!
</code></pre>
</li>
</ol>
<h2>Projects now use System.Text.Json by default</h2>
<p>New ASP.NET Core projects will now use System.Text.Json for JSON handling by default. In this release we removed Json.NET (Newtonsoft.Json) from the project templates. To enable support for using Json.NET, add the Microsoft.AspNetCore.Mvc.NewtonsoftJson package to your project and add a call to <code>AddNewtonsoftJson()</code> following code in your <code>Startup.ConfigureServices</code> method. For example:</p>
<pre><code class="csharp">services.AddMvc() .AddNewtonsoftJson();
</code></pre>
<h2>Certificate and Kerberos authentication</h2>
<p>Preview 6 brings Certificate and Kerberos authentication to ASP.NET Core.</p>
<p>Certificate authentication requires you to configure your server to accept certificates, and then add the authentication middleware in <code>Startup.Configure</code> and the certificate authentication service in <code>Startup.ConfigureServices</code>.</p>
<pre><code class="csharp">public void ConfigureServices(IServiceCollection services)
{ services.AddAuthentication( CertificateAuthenticationDefaults.AuthenticationScheme) .AddCertificate(); // All the other service configuration.
} public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{ app.UseAuthentication(); // All the other app configuration.
}
</code></pre>
<p>Options for certificate authentication include the ability to accept self-signed certificates, check for certificate revocation, and check that the proffered certificate has the right usage flags in it. A default user principal is constructed from the certificate properties, with an event that enables you to supplement or replace the principal. All the options, and instructions on how to configure common hosts for certificate authentication can be found in the <a href="https://docs.microsoft.com/en-us/aspnet/core/security/authentication/certauth?view=aspnetcore-3.0">documentation</a>.</p>
<p>We’ve also extended “Windows Authentication” onto Linux and macOS. Previously this authentication type was limited to IIS and HttpSys, but now Kestrel has the ability to use Negotiate, Kerberos, and NTLM on Windows, Linux, and macOS for Windows domain joined hosts by using the Microsoft.AspNetCore.Authentication.Negotiate nuget package. As with the other authentication services you configure authentication app wide, then configure the service:</p>
<pre><code class="csharp">public void ConfigureServices(IServiceCollection services)
{ services.AddAuthentication(NegotiateDefaults.AuthenticationScheme) .AddNegotiate();
} public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{ app.UseAuthentication(); // All the other app configuration.
}
</code></pre>
<p>Your host must be configured correctly. Windows hosts must have SPNs added to the user account hosting the application. Linux and macOS machines must be joined to the domain, then SPNs must be created for the web process, as well as <a href="https://blogs.technet.microsoft.com/pie/2018/01/03/all-you-need-to-know-about-keytab-files/">keytab</a> files generated and configured on the host machine. Full instructions are given in the <a href="https://docs.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.0">documentation</a>.</p>
<h2>SignalR Auto-reconnect</h2>
<p>This preview release, available now via <code>npm install @aspnet/signalr@next</code> and in the .NET Core SignalR Client, includes a new automatic reconnection feature. With this release we’ve added the <code>withAutomaticReconnect()</code> method to the <code>HubConnectionBuilder</code>. By default, the client will try to reconnect immediately and after 2, 10, and 30 seconds. Enlisting in automatic reconnect is opt-in, but simple via this new method.</p>
<pre><code class="javascript">const connection = new signalR.HubConnectionBuilder() .withUrl("/chatHub") .withAutomaticReconnect() .build();
</code></pre>
<p>By passing an array of millisecond-based durations to the method, you can be very granular about how your reconnection attempts occur over time.</p>
<pre><code class="javascript">.withAutomaticReconnect([0, 3000, 5000, 10000, 15000, 30000])
//.withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior
</code></pre>
<p>Or you can pass in an implementation of a custom reconnect policy that gives you full control.</p>
<p>If the reconnection fails after the 30-second point (or whatever you’ve set as your maximum), the client presumes the connection is offline and stops trying to reconnect. During these reconnection attempts you’ll want to update your application UI to provide cues to the user that the reconnection is being attempted.</p>
<h3>Reconnection Event Handlers</h3>
<p>To make this easier, we’ve expanded the SignalR client API to include <code>onreconnecting</code> and <code>onreconnected</code> event handlers. The first of these handlers, <code>onreconnecting</code>, gives developers a good opportunity to disable UI or to let users know the app is offline.</p>
<pre><code class="javascript">connection.onreconnecting((error) => { const status = `Connection lost due to error "${error}". Reconnecting.`; document.getElementById("messageInput").disabled = true; document.getElementById("sendButton").disabled = true; document.getElementById("connectionStatus").innerText = status;
});
</code></pre>
<p>Likewise, the <code>onreconnected</code> handler gives developers an opportunity to update the UI once the connection is reestablished.</p>
<pre><code class="javascript">connection.onreconnected((connectionId) => { const status = `Connection reestablished. Connected.`; document.getElementById("messageInput").disabled = false; document.getElementById("sendButton").disabled = false; document.getElementById("connectionStatus").innerText = status;
});
</code></pre>
<h3>Learn more about customizing and handling reconnection</h3>
<p>Automatic reconnect has been partially documented already in the preview release. Check out the deeper docs on the topic, with more examples and details on usage, at <a href="https://aka.ms/signalr/auto-reconnect">https://aka.ms/signalr/auto-reconnect</a>.</p>
<h2>Managed gRPC Client</h2>
<p>In prior previews, we relied on the <code>Grpc.Core</code> library for client support. The addition of HTTP/2 support in <code>HttpClient</code> in this preview has allowed us to introduce a fully managed gRPC client.</p>
<p>To begin using the new client, add a package reference to <code>Grpc.Net.Client</code> and then you can create a new client.</p>
<pre><code class="csharp">var httpClient = new HttpClient() { BaseAddress = new Uri("https://localhost:5001") };
var client = GrpcClient.Create<GreeterClient>(httpClient);
</code></pre>
<h2>gRPC Client Factory</h2>
<p>Building on the opinionated pattern we introduced in <code>HttpClientFactory</code>, we’ve added a gRPC client factory for creating gRPC client instances in your project. There are two flavors of the factory that we’ve added: <code>Grpc.Net.ClientFactory</code> and <code>Grpc.AspNetCore.Server.ClientFactory</code>.</p>
<p>The <code>Grpc.Net.ClientFactory</code> is designed for use in non-ASP.NET app models (such as Worker Services) that still use the <code>Microsoft.Extensions.*</code> primitives without a dependency on ASP.NET Core.</p>
<p>In applications that perform service-to-service communication, we often observe that most servers are also clients that consume other services. In these scenarios, we recommend the use of <code>Grpc.AspNetCore.Server.ClientFactory</code> which features automatic propagation of gRPC deadlines and cancellation tokens.</p>
<p>To use the client factory, add the appropriate package reference to your project (<code>Grpc.AspNetCore.Server.Factory</code> or <code>Grpc.Net.ClientFactory</code>) before adding the following code to <code>ConfigureServices()</code>.</p>
<pre><code class="csharp">services .AddGrpcClient<GreeterClient>(options => { options.BaseAddress = new Uri("https://localhost:5001"); });
</code></pre>
<h2>gRPC Interceptors</h2>
<p>gRPC exposes a mechanism to intercept RPC invocations on both the client and the server. Interceptors can be used in conjunction with existing HTTP middleware. Unlike HTTP middleware, interceptors give you access to actual request/response objects before serialization (on the client) and after deserialization (on the server) and vice versa for the response. All middlewares run before interceptors on the request side and vice versa on the response side.</p>
<h3>Client interceptors</h3>
<p>When used in conjunction with the client factory, you can add a client interceptor as shown below.</p>
<pre><code class="csharp">services .AddGrpcClient<GreeterClient>(options => { options.BaseAddress = new Uri("https://localhost:5001"); }) .AddInterceptor<CallbackInterceptor>();
</code></pre>
<h3>Server interceptors</h3>
<p>Server interceptors can be registered in <code>ConfigureServices()</code> as shown below.</p>
<pre><code class="csharp">services .AddGrpc(options => { // This registers a global interceptor options.Interceptors.Add<MaxStreamingRequestTimeoutInterceptor>(TimeSpan.FromSeconds(30)); }) .AddServiceOptions<GreeterService>(options => { // This registers an interceptor for the Greeter service options.Interceptors.Add<UnaryCachingInterceptor>(); });
</code></pre>
<p>For examples on how to author an interceptors, take a look at <a href="https://github.com/grpc/grpc-dotnet/tree/master/examples/Server/Interceptors">these examples</a> in the grpc-dotnet repo.</p>
<h2>Give feedback</h2>
<p>We hope you enjoy the new features in this preview release of ASP.NET Core and Blazor! Please let us know what you think by filing issues on <a href="https://github.com/aspnet/aspnetcore/issues">GitHub</a>.</p>
<p>Thanks for trying out ASP.NET Core and Blazor!</p>
<div class="authorinfoarea">
<div><img src="http://www.sickgaming.net/blog/wp-content/uploads/2019/06/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6.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" target="_blank" href="https://devblogs.microsoft.com/aspnet/author/danroth27/" rel="noopener noreferrer">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>