{"id":25061,"date":"2021-07-14T09:32:41","date_gmt":"2021-07-14T16:32:41","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/aspnet\/?p=25061"},"modified":"2021-07-14T09:32:41","modified_gmt":"2021-07-14T16:32:41","slug":"asp-net-core-updates-in-net-6-preview-6","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/asp-net-core-updates-in-net-6-preview-6\/","title":{"rendered":"ASP.NET Core updates in .NET 6 Preview 6"},"content":{"rendered":"<p><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-net-6-preview-6\/\">.NET 6 Preview 6 is now available<\/a> and includes many great new improvements to ASP.NET Core.<\/p>\n<p>Here&#8217;s what&#8217;s new in this preview release:<\/p>\n<ul>\n<li>Improved Blazor accessibility<\/li>\n<li>Required Blazor component parameters<\/li>\n<li>Efficient byte array transfers for JavaScript interop<\/li>\n<li>Optional parameters for view component tag helpers<\/li>\n<li>Angular template updated to Angular 12<\/li>\n<li>OpenAPI support for minimal APIs<\/li>\n<li>Inject services into minimal APIs without <code>[FromServices]<\/code> attribute<\/li>\n<li>Configure the accept socket for Kestrel<\/li>\n<li><code>IHttpActivityFeature<\/code><\/li>\n<li>Long running activity tag for SignalR connections<\/li>\n<li>WebSocket compression<\/li>\n<li>SignalR WebSockets TestServer support<\/li>\n<li>New <code>OnCheckSlidingExpiration<\/code> event for controlling cookie renewal<\/li>\n<li>ClientCertificateMode.DelayCertificate<\/li>\n<\/ul>\n<h2>Get started<\/h2>\n<p>To get started with ASP.NET Core in .NET 6 Preview 6, <a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet\/6.0\">install the .NET 6 SDK<\/a>.<\/p>\n<p>If you&#8217;re on Windows using Visual Studio, we recommend <a href=\"https:\/\/aka.ms\/vs2022preview\">installing the latest preview of Visual Studio 2022<\/a>. If you&#8217;re on macOS, we recommend <a href=\"https:\/\/docs.microsoft.com\/visualstudio\/mac\/install-preview\">installing the latest preview of Visual Studio 2019 for Mac 8.10<\/a>.<\/p>\n<p>To get setup with .NET MAUI &amp; Blazor for cross-platform native apps, see the latest instructions in the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/maui\/get-started\/installation\">.NET MAUI getting started guide<\/a>. Be sure to also check out the <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-net-maui-preview-6\/\">Announcing .NET MAUI Preview 6<\/a> blog post for all the details on what&#8217;s new in .NET MAUI in this release.<\/p>\n<p>To install the latest .NET WebAssembly tools for ahead-of-time (AOT) compilation and runtime relinking, run the following command from an elevated command prompt:<\/p>\n<pre><code>dotnet workload install microsoft-net-sdk-blazorwebassembly-aot\n<\/code><\/pre>\n<p>If you&#8217;ve installed the .NET WebAssembly tools workload previously, you can update it to .NET 6 Preview 5 by running the following command from an elevated command prompt:<\/p>\n<pre><code>dotnet workload update\n<\/code><\/pre>\n<h2>Upgrade an existing project<\/h2>\n<p>To upgrade an existing ASP.NET Core app from .NET 6 Preview 5 to .NET 6 Preview 6:<\/p>\n<ul>\n<li>Update all Microsoft.AspNetCore.&#042; package references to <code>6.0.0-preview.6.*<\/code>.<\/li>\n<li>Update all Microsoft.Extensions.&#042; package references to <code>6.0.0-preview.6.*<\/code>.<\/li>\n<\/ul>\n<p>To upgrade a .NET MAUI Blazor app from .NET 6 Preview 5 to .NET 6 Preview 6 we recommend starting from a new .NET MAUI Blazor project created with the .NET 6 Preview 5 SDK and then copying code over from your original project.<\/p>\n<p>See the full list of <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/compatibility\/6.0#aspnet-core\">breaking changes<\/a> in ASP.NET Core for .NET 6.<\/p>\n<h2>Improved Blazor accessibility<\/h2>\n<p>We made a number of changes to the default Blazor template to improve accessibility when using a screen reader:<\/p>\n<ul>\n<li>We added <code>role=\"alert\"<\/code> attributes to messages that should be announced when navigating to a page and removed <code>role=\"alert\"<\/code> from the survey prompt component to deemphasize its content.<\/li>\n<li>We updated the <code>Counter<\/code> component to add <code>role=\"status\"<\/code> so that the current count is read as the counter gets updated.<\/li>\n<li>We switched to using more semantic markup elements where appropriate, like <code>main<\/code> and <code>article<\/code>.<\/li>\n<li>We swapped out the <code>ul<\/code> in the <code>NavBar<\/code> component for a <code>nav<\/code> so that its semantics are easier to identify and so it can be jumped to directly using common screen reader keyboard shortcuts.<\/li>\n<li>We added a <code>title<\/code> to the <code>NavBar<\/code> toggle button so that it&#8217;s purpose is clearly announced.<\/li>\n<\/ul>\n<p>We also added a new <code>FocusOnNavigate<\/code> component to Blazor, that sets the UI focus to an element based on a CSS selector after navigating from one page to another. You can see the <code>FocusOnNavigate<\/code> component at work in the <code>App<\/code> component in the default Blazor template:<\/p>\n<pre><code class=\"razor\">&lt;Router AppAssembly=\"@typeof(Program).Assembly\"&gt;\n    &lt;Found Context=\"routeData\"&gt;\n        &lt;RouteView RouteData=\"@routeData\" DefaultLayout=\"@typeof(MainLayout)\" \/&gt;\n        &lt;FocusOnNavigate RouteData=\"@routeData\" Selector=\"h1\" \/&gt;\n    &lt;\/Found&gt;\n    &lt;NotFound&gt;\n        &lt;LayoutView Layout=\"@typeof(MainLayout)\"&gt;\n            &lt;p role=\"alert\"&gt;Sorry, there's nothing at this address.&lt;\/p&gt;\n        &lt;\/LayoutView&gt;\n    &lt;\/NotFound&gt;\n&lt;\/Router&gt;\n<\/code><\/pre>\n<p>When the <code>Router<\/code> navigates to a new page, the <code>FocusOnNavigate<\/code> component sets the focus on the top-level header for that page, which gets read by screen readers. This is a common strategy in single-page apps for making sure that page navigations are appropriately announced when using a screen reader.<\/p>\n<p>We&#8217;re always looking for ways to to improve the accessibility of apps built with ASP.NET Core &amp; Blazor. If you have ideas on how to further improve accessibility, you can share your suggestions with us on <a href=\"https:\/\/github.com\/dotnet\/aspnetcore\/issues\/new\">GitHub<\/a>. You can also learn more about building accessible apps with .NET by checking out the recent <a href=\"https:\/\/youtu.be\/5ceTua82w4s?t=136\">ASP.NET Community Standup on Accessibility for Web Developers<\/a> or by watching the recent <a href=\"https:\/\/youtu.be\/ttxAYDMKtSs?t=163\">Let&#8217;s Learn .NET: Accessibility<\/a> event.<\/p>\n<h2>Required Blazor component parameters<\/h2>\n<p>To require that a Blazor component parameter be specified when using the component, use the new <code>[EditorRequired]<\/code> attribute:<\/p>\n<p><em>SurveyPrompt.razor<\/em><\/p>\n<pre><code class=\"razor\">[EditorRequired]\n[Parameter]\npublic string Title { get; set; }\n<\/code><\/pre>\n<p>If the user tries to use the component without specifying the required parameter, they get a warning:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2021\/07\/required-params.png\" alt=\"Required parameter\" \/><\/p>\n<p>The <code>[EditorRequired]<\/code> attribute is enforced at design-time and as part of the build. It isn&#8217;t enforced at runtime and it doesn&#8217;t guarantee the the parameter value cannot be null.<\/p>\n<h2>Efficient byte array transfer in JavaScript interop<\/h2>\n<p>Blazor now has more efficient support for byte arrays when performing JavaScript interop. Previously, byte arrays sent to and from JavaScript were Base64 encoded so they could be serialized as JSON, which increased the transfer size and the CPU load. This encoding has now been optimized away in .NET 6. The new byte array optimization is transparent to the user when passing a <code>Uint8Array<\/code> from JavaScript to .NET.<\/p>\n<p>When passing a <code>byte[]<\/code> from .NET to JavaScript, the bytes are now received as a <code>Uint8Array<\/code> instead of as a Base64 encoded string. Code that previously decoded the Base64 encoded string will need to be removed. See the related <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/core\/compatibility\/aspnet-core\/6.0\/byte-array-interop\">announcement<\/a> for details on this breaking change.<\/p>\n<h2>Optional parameters for view components tag helpers<\/h2>\n<p>View component tag helpers now support optional parameters. If your view component has an optional parameter, you no longer need to specify a value for that parameter as a tag helper attribute.<\/p>\n<p>So, if you have a view component with an optional parameter like this:<\/p>\n<pre><code class=\"csharp\">class MyViewComponent\n{\n    IViewComponentResult Invoke(bool showSomething = false) { ... }\n}\n<\/code><\/pre>\n<p>then you can now invoke it as a tag helper without having to specify a value for the <code>showSomething<\/code> parameter:<\/p>\n<pre><code class=\"cshtml\">&lt;vc:my \/&gt;\n<\/code><\/pre>\n<h2>Angular template updated to Angular 12<\/h2>\n<p>The ASP.NET Core template for Angular now uses Angular 12.<\/p>\n<h2>OpenAPI support in minimal APIs<\/h2>\n<p>In <a href=\"https:\/\/devblogs.microsoft.com\/aspnet\/asp-net-core-updates-in-net-6-preview-4\/#introducing-minimal-apis\">.NET 6 preview 4<\/a>, we announced minimal APIs for hosting and routing in web applications. We shared how one could develop an API in a single file with just a few lines of code. Our new streamlined APIs provide the benefits of ASP.NET with less ceremony.<\/p>\n<p>Today we are excited that minimal APIs now have support for <a href=\"https:\/\/www.openapis.org\/\">OpenAPI<\/a>. With OpenAPI (Swagger) support, you can now easily set up <a href=\"https:\/\/swagger.io\/tools\/swagger-ui\/\">Swagger UI<\/a> to visualize and interact with minimal APIs.<\/p>\n<h3>Configure Swagger UI with minimal APIs<\/h3>\n<p>To try OpenAPI with minimal APIs, create a new ASP.NET Core empty web app using the .NET CLI <code>dotnet new web -o MyApi<\/code> or select &#8220;ASP.NET Core Empty&#8221; in Visual Studio.<\/p>\n<p><strong>Add Swagger UI to your application<\/strong><\/p>\n<p>Add the <code>Swashbuckle.AspNetCore<\/code> package to your application<\/p>\n<pre><code class=\"console\">dotnet add package Swashbuckle.AspNetCore\n<\/code><\/pre>\n<p>Open <code>Program.cs<\/code> and update the following configurations to set up Swagger in you application; you will need to update the following:<\/p>\n<p><strong>Dependency Injection<\/strong>: Add the Swagger document generator <code>AddSwaggerGen<\/code> to <code>builder.services<\/code>.<\/p>\n<pre><code class=\"csharp\">builder.Services.AddEndpointsApiExplorer();\n\nbuilder.Services.AddSwaggerGen(c =&gt;\n{\n    c.SwaggerDoc(\"v1\", new OpenApiInfo { Title = \"My API\", Description = \"Docs for my API\", Version = \"v1\" });\n});\n<\/code><\/pre>\n<p><strong>Middleware<\/strong>: Call <code>app.UseSwagger<\/code> and <code>app.UseSwaggerUI<\/code> to add the Swagger document and UI middleware to your application.<\/p>\n<pre><code class=\"csharp\">app.UseSwagger();\n\napp.UseSwaggerUI(c =&gt;\n{\n    c.SwaggerEndpoint(\"\/swagger\/v1\/swagger.json\", \"My API V1\");\n});\n<\/code><\/pre>\n<p>Run your application and navigate to this URL https:\/\/localhost:5001\/swagger.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/user-images.githubusercontent.com\/2546640\/125470995-c68985a7-fddd-4f7c-b574-f5ce200f4928.gif\" alt=\"swagger-P6\" \/><\/p>\n<p>Now that Swagger UI is setup, you can visualize and interact with your API.<\/p>\n<p>The screenshot below is for a more fully featured API that operates with a SQLite database. We will see more of these features in .NET 6 Preview 7.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/user-images.githubusercontent.com\/2546640\/125499003-f1041f4d-22ef-4b48-b391-431803865be4.gif\" alt=\"swagger-P6-HTTPMethods\" \/><\/p>\n<h2>Inject services into minimal APIs without <code>[FromServices]<\/code> attribute<\/h2>\n<p>This preview also allows developers to inject services to their routing handlers without the need for the <code>[FromServices]<\/code> attribute.<\/p>\n<p><strong>Before:<\/strong> Code with <code>[FromServices]<\/code> attribute<\/p>\n<pre><code class=\"csharp\">app.MapGet(\"\/todos\", async ([FromServices] TodoDbContext  db) =&gt;\n{\n    return await db.Todos.ToListAsync();\n});\n<\/code><\/pre>\n<p><strong>After:<\/strong> Code without <code>[FromServices]<\/code> attribute<\/p>\n<pre><code class=\"csharp\">app.MapGet(\"\/todos\", async (TodoDbContext db) =&gt;\n{\n    return await db.Todos.ToListAsync();\n});\n<\/code><\/pre>\n<p>We&#8217;ve added a new capability to the dependency injection container called <code>IServiceProviderIsService<\/code>. This capability allows developers to query the container to determine if a type is resolvable. The <a href=\"https:\/\/devblogs.microsoft.com\/aspnet\/asp-net-core-updates-in-net-6-preview-4\/#new-routing-apis\">new routing APIs<\/a> use this capability to determine if a parameter can be bound from services.<\/p>\n<p>The code below shows how you can use the <code>IServiceProviderIsService<\/code> capability to detect if <code>MyService<\/code> and <code>NotAService<\/code> are considered services by the dependency injection container:<\/p>\n<pre><code class=\"csharp\">var serviceProvider = new ServiceCollection()\n    .AddSingleton&lt;MyService&gt;()\n    .BuildServiceProvider();\n\nvar detector = serviceProvider.GetService&lt;IServiceProviderIsService&gt;();\nif (detector is not null)\n{\n    Console.WriteLine($\"MyService is a service = {detector.IsService(typeof(MyService))}\");\n    Console.WriteLine($\"NotAService is a service = {detector.IsService(typeof(NotAService))}\");\n}\n\nclass MyService { }\nclass NotAService { }\n<\/code><\/pre>\n<h2>Configure the accept socket for Kestrel<\/h2>\n<p>We&#8217;ve added a callback on <code>SocketTransportOptions<\/code> that allows you to control creation of the accept socket by creating your own socket and binding to the specified endpoint. If you only want to mutate the socket after it&#8217;s bound, you can call the <code>CreateDefaultBoundListenSocket<\/code> static helper method on <code>SocketTransportOptions<\/code>.<\/p>\n<pre><code class=\"csharp\">var builder = WebApplication.CreateBuilder(args);\nbuilder.WebHost.UseSockets(options =&gt;\n{\n    options.CreateBoundListenSocket = (endpoint) =&gt;\n    {\n        var socket = SocketTransportOptions.CreateDefaultBoundListenSocket(endpoint);\n        \/\/ Modify listen socket\n        return socket;\n    };\n});\n<\/code><\/pre>\n<h2><code>IHttpActivityFeature<\/code><\/h2>\n<p>Hosting creates an <code>Activity<\/code> every request when there are diagnostic listeners present. There is now a feature to allow middleware to access this activity so additional information can be included during request processing.<\/p>\n<pre><code class=\"csharp\">app.Run(context =&gt;\n{\n    var activity = context.Features.Get&lt;IHttpActivityFeature&gt;()?.Activity;\n    if (activity is not null)\n    {\n        activity.AddTag(\"some_info\", \"true\");\n    }\n});\n<\/code><\/pre>\n<h2>Long running activity tag for SignalR connections<\/h2>\n<p>SignalR uses the new <code>IHttpActivityFeature<\/code> to add an &#8220;http.long_running&#8221; tag to the request activity. This will be used by APM services like Azure Monitor Application Insights to filter SignalR requests from creating long running request alerts.<\/p>\n<h2>WebSocket compression<\/h2>\n<p>You can now optionally accept WebSocket connections that use compression. Compression is off by default because enabling compression over encrypted connections can make the app subject to CRIME\/BREACH attacks. It should only be enabled if you know that sensitive information isn&#8217;t being sent or if you turn off compression for the messages that contain sensitive information with the <code>System.Net.WebSockets.WebSocketMessageFlags.DisableCompression<\/code> flag in the <code>SendAsync<\/code> overload. Note: Clients would also need to disable compression when sending sensitive information which is not an option when using WebSockets in the browser.<\/p>\n<pre><code class=\"csharp\">app.Run(context =&gt;\n{\n    if (context.WebSockets.IsWebSocketRequest)\n    {\n        using var websocket = await context.WebSockets.AcceptWebSocketAsync(\n            new WebSocketAcceptContext() { DangerousEnableCompression = true });\n        await websocket.SendAsync(...);\n        await websocket.ReceiveAsync(...);\n        await websocket.CloseAsync(WebSocketCloseStatus.NormalClosure, null, default);\n    }\n});\n<\/code><\/pre>\n<h2>SignalR WebSockets TestServer Support<\/h2>\n<p>We have added <code>WebSocketFactory<\/code> to the options when creating a .NET SignalR connection. This option can be used with <code>TestServer<\/code> to allow testing with the WebSocket transport.<\/p>\n<pre><code class=\"csharp\">WebApplicationFactory&lt;Startup&gt; factory;\nvar connection = new HubConnectionBuilder()\n    .WithUrl(\"http:\/\/localhost:53353\/echo\", options =&gt;\n    {\n        options.Transports = HttpTransportType.WebSockets;\n        options.HttpMessageHandlerFactory = _ =&gt;\n        {\n            return factory.Server.CreateHandler();\n        };\n        options.WebSocketFactory = async (context, token) =&gt;\n        {\n            var wsClient = factory.Server.CreateWebSocketClient();\n            return await wsClient.ConnectAsync(context.Uri, default);\n        };\n    })\n    .Build();\n<\/code><\/pre>\n<h2>New <code>OnCheckSlidingExpiration<\/code> event for controlling cookie renewal<\/h2>\n<p>Cookie authentication sliding expiration can now be customized or suppressed using the new <code>OnCheckSlidingExpiration<\/code>. For example, this event can be used by a single-page app that needs to periodically ping the server without affecting the authentication session.<\/p>\n<h2>Delayed client certificate negotiation<\/h2>\n<p>Developers can now opt-in to using delayed client certificate negotiation by specifying <code>ClientCertificateMode.DelayCertificate<\/code> on the <code>HttpsConnectionAdapterOptions<\/code>. This will only work with HTTP\/1.1 connections since HTTP\/2 strictly forbids delayed certificate renegotiation. The caller of this API must buffer the request body before requesting the client certificate.<\/p>\n<pre><code class=\"csharp\">var builder = WebApplication.CreateBuilder(args);\nbuilder.WebHost.UseKestrel(options =&gt;\n{\n    options.ConfigureHttpsDefaults(adapterOptions =&gt;\n    {\n        adapterOptions.ClientCertificateMode = ClientCertificateMode.DelayCertificate;\n    });\n});\n\nvar app = builder.Build();\napp.Use(async (context, next) =&gt;\n{\n    \/\/ Check if your desired criteria is met\n    if (desiredState == true)\n    {\n        \/\/ Buffer the request body\n        context.Request.EnableBuffering();\n        var body = context.Request.Body;\n        await body.DrainAsync(context.RequestAborted);\n        body.Position = 0;\n\n        \/\/ Request client certificate\n        var cert = await context.Connection.GetClientCertificateAsync();\n\n        \/\/  Disable buffering on future requests if the client doesn't provide a cert\n    } \n    return next(context);\n});\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\napp.Run();\n<\/code><\/pre>\n<h2>Give feedback<\/h2>\n<p>We hope you enjoy this preview release of ASP.NET Core in .NET 6. We&#8217;re eager to hear about your experiences with this release. Let us know what you think by filing issues on <a href=\"https:\/\/github.com\/dotnet\/aspnetcore\/issues\">GitHub<\/a>.<\/p>\n<p>Thanks for trying out ASP.NET Core!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>.NET 6 Preview 6 is now available! Check out all the improvements in ASP.NET Core in this update.<\/p>\n","protected":false},"author":417,"featured_media":21413,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[197,7509,7251],"tags":[],"class_list":["post-25061","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aspnet","category-aspnetcore","category-blazor"],"acf":[],"blog_post_summary":"<p>.NET 6 Preview 6 is now available! Check out all the improvements in ASP.NET Core in this update.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/25061","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/417"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=25061"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/25061\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/21413"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=25061"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=25061"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=25061"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}