July 14th, 2021

ASP.NET Core updates in .NET 6 Preview 6

Daniel Roth
Principal Product Manager

.NET 6 Preview 6 is now available and includes many great new improvements to ASP.NET Core.

Here’s what’s new in this preview release:

  • Improved Blazor accessibility
  • Required Blazor component parameters
  • Efficient byte array transfers for JavaScript interop
  • Optional parameters for view component tag helpers
  • Angular template updated to Angular 12
  • OpenAPI support for minimal APIs
  • Inject services into minimal APIs without [FromServices] attribute
  • Configure the accept socket for Kestrel
  • IHttpActivityFeature
  • Long running activity tag for SignalR connections
  • WebSocket compression
  • SignalR WebSockets TestServer support
  • New OnCheckSlidingExpiration event for controlling cookie renewal
  • ClientCertificateMode.DelayCertificate

Get started

To get started with ASP.NET Core in .NET 6 Preview 6, install the .NET 6 SDK.

If you’re on Windows using Visual Studio, we recommend installing the latest preview of Visual Studio 2022. If you’re on macOS, we recommend installing the latest preview of Visual Studio 2019 for Mac 8.10.

To get setup with .NET MAUI & Blazor for cross-platform native apps, see the latest instructions in the .NET MAUI getting started guide. Be sure to also check out the Announcing .NET MAUI Preview 6 blog post for all the details on what’s new in .NET MAUI in this release.

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:

dotnet workload install microsoft-net-sdk-blazorwebassembly-aot

If you’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:

dotnet workload update

Upgrade an existing project

To upgrade an existing ASP.NET Core app from .NET 6 Preview 5 to .NET 6 Preview 6:

  • Update all Microsoft.AspNetCore.* package references to 6.0.0-preview.6.*.
  • Update all Microsoft.Extensions.* package references to 6.0.0-preview.6.*.

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.

See the full list of breaking changes in ASP.NET Core for .NET 6.

Improved Blazor accessibility

We made a number of changes to the default Blazor template to improve accessibility when using a screen reader:

  • We added role="alert" attributes to messages that should be announced when navigating to a page and removed role="alert" from the survey prompt component to deemphasize its content.
  • We updated the Counter component to add role="status" so that the current count is read as the counter gets updated.
  • We switched to using more semantic markup elements where appropriate, like main and article.
  • We swapped out the ul in the NavBar component for a nav so that its semantics are easier to identify and so it can be jumped to directly using common screen reader keyboard shortcuts.
  • We added a title to the NavBar toggle button so that it’s purpose is clearly announced.

We also added a new FocusOnNavigate 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 FocusOnNavigate component at work in the App component in the default Blazor template:

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p role="alert">Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

When the Router navigates to a new page, the FocusOnNavigate 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.

We’re always looking for ways to to improve the accessibility of apps built with ASP.NET Core & Blazor. If you have ideas on how to further improve accessibility, you can share your suggestions with us on GitHub. You can also learn more about building accessible apps with .NET by checking out the recent ASP.NET Community Standup on Accessibility for Web Developers or by watching the recent Let’s Learn .NET: Accessibility event.

Required Blazor component parameters

To require that a Blazor component parameter be specified when using the component, use the new [EditorRequired] attribute:

SurveyPrompt.razor

[EditorRequired]
[Parameter]
public string Title { get; set; }

If the user tries to use the component without specifying the required parameter, they get a warning:

Required parameter

The [EditorRequired] attribute is enforced at design-time and as part of the build. It isn’t enforced at runtime and it doesn’t guarantee the the parameter value cannot be null.

Efficient byte array transfer in JavaScript interop

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 Uint8Array from JavaScript to .NET.

When passing a byte[] from .NET to JavaScript, the bytes are now received as a Uint8Array instead of as a Base64 encoded string. Code that previously decoded the Base64 encoded string will need to be removed. See the related announcement for details on this breaking change.

Optional parameters for view components tag helpers

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.

So, if you have a view component with an optional parameter like this:

class MyViewComponent
{
    IViewComponentResult Invoke(bool showSomething = false) { ... }
}

then you can now invoke it as a tag helper without having to specify a value for the showSomething parameter:

<vc:my />

Angular template updated to Angular 12

The ASP.NET Core template for Angular now uses Angular 12.

OpenAPI support in minimal APIs

In .NET 6 preview 4, 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.

Today we are excited that minimal APIs now have support for OpenAPI. With OpenAPI (Swagger) support, you can now easily set up Swagger UI to visualize and interact with minimal APIs.

Configure Swagger UI with minimal APIs

To try OpenAPI with minimal APIs, create a new ASP.NET Core empty web app using the .NET CLI dotnet new web -o MyApi or select “ASP.NET Core Empty” in Visual Studio.

Add Swagger UI to your application

Add the Swashbuckle.AspNetCore package to your application

dotnet add package Swashbuckle.AspNetCore

Open Program.cs and update the following configurations to set up Swagger in you application; you will need to update the following:

Dependency Injection: Add the Swagger document generator AddSwaggerGen to builder.services.

builder.Services.AddEndpointsApiExplorer();

builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Description = "Docs for my API", Version = "v1" });
});

Middleware: Call app.UseSwagger and app.UseSwaggerUI to add the Swagger document and UI middleware to your application.

app.UseSwagger();

app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});

Run your application and navigate to this URL https://localhost:5001/swagger.

swagger-P6

Now that Swagger UI is setup, you can visualize and interact with your API.

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.

swagger-P6-HTTPMethods

Inject services into minimal APIs without [FromServices] attribute

This preview also allows developers to inject services to their routing handlers without the need for the [FromServices] attribute.

Before: Code with [FromServices] attribute

app.MapGet("/todos", async ([FromServices] TodoDbContext  db) =>
{
    return await db.Todos.ToListAsync();
});

After: Code without [FromServices] attribute

app.MapGet("/todos", async (TodoDbContext db) =>
{
    return await db.Todos.ToListAsync();
});

We’ve added a new capability to the dependency injection container called IServiceProviderIsService. This capability allows developers to query the container to determine if a type is resolvable. The new routing APIs use this capability to determine if a parameter can be bound from services.

The code below shows how you can use the IServiceProviderIsService capability to detect if MyService and NotAService are considered services by the dependency injection container:

var serviceProvider = new ServiceCollection()
    .AddSingleton<MyService>()
    .BuildServiceProvider();

var detector = serviceProvider.GetService<IServiceProviderIsService>();
if (detector is not null)
{
    Console.WriteLine($"MyService is a service = {detector.IsService(typeof(MyService))}");
    Console.WriteLine($"NotAService is a service = {detector.IsService(typeof(NotAService))}");
}

class MyService { }
class NotAService { }

Configure the accept socket for Kestrel

We’ve added a callback on SocketTransportOptions 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’s bound, you can call the CreateDefaultBoundListenSocket static helper method on SocketTransportOptions.

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseSockets(options =>
{
    options.CreateBoundListenSocket = (endpoint) =>
    {
        var socket = SocketTransportOptions.CreateDefaultBoundListenSocket(endpoint);
        // Modify listen socket
        return socket;
    };
});

IHttpActivityFeature

Hosting creates an Activity 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.

app.Run(context =>
{
    var activity = context.Features.Get<IHttpActivityFeature>()?.Activity;
    if (activity is not null)
    {
        activity.AddTag("some_info", "true");
    }
});

Long running activity tag for SignalR connections

SignalR uses the new IHttpActivityFeature to add an “http.long_running” 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.

WebSocket compression

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’t being sent or if you turn off compression for the messages that contain sensitive information with the System.Net.WebSockets.WebSocketMessageFlags.DisableCompression flag in the SendAsync overload. Note: Clients would also need to disable compression when sending sensitive information which is not an option when using WebSockets in the browser.

app.Run(context =>
{
    if (context.WebSockets.IsWebSocketRequest)
    {
        using var websocket = await context.WebSockets.AcceptWebSocketAsync(
            new WebSocketAcceptContext() { DangerousEnableCompression = true });
        await websocket.SendAsync(...);
        await websocket.ReceiveAsync(...);
        await websocket.CloseAsync(WebSocketCloseStatus.NormalClosure, null, default);
    }
});

SignalR WebSockets TestServer Support

We have added WebSocketFactory to the options when creating a .NET SignalR connection. This option can be used with TestServer to allow testing with the WebSocket transport.

WebApplicationFactory<Startup> factory;
var connection = new HubConnectionBuilder()
    .WithUrl("http://localhost:53353/echo", options =>
    {
        options.Transports = HttpTransportType.WebSockets;
        options.HttpMessageHandlerFactory = _ =>
        {
            return factory.Server.CreateHandler();
        };
        options.WebSocketFactory = async (context, token) =>
        {
            var wsClient = factory.Server.CreateWebSocketClient();
            return await wsClient.ConnectAsync(context.Uri, default);
        };
    })
    .Build();

New OnCheckSlidingExpiration event for controlling cookie renewal

Cookie authentication sliding expiration can now be customized or suppressed using the new OnCheckSlidingExpiration. For example, this event can be used by a single-page app that needs to periodically ping the server without affecting the authentication session.

Delayed client certificate negotiation

Developers can now opt-in to using delayed client certificate negotiation by specifying ClientCertificateMode.DelayCertificate on the HttpsConnectionAdapterOptions. 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.

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseKestrel(options =>
{
    options.ConfigureHttpsDefaults(adapterOptions =>
    {
        adapterOptions.ClientCertificateMode = ClientCertificateMode.DelayCertificate;
    });
});

var app = builder.Build();
app.Use(async (context, next) =>
{
    // Check if your desired criteria is met
    if (desiredState == true)
    {
        // Buffer the request body
        context.Request.EnableBuffering();
        var body = context.Request.Body;
        await body.DrainAsync(context.RequestAborted);
        body.Position = 0;

        // Request client certificate
        var cert = await context.Connection.GetClientCertificateAsync();

        //  Disable buffering on future requests if the client doesn't provide a cert
    } 
    return next(context);
});
app.MapGet("/", () => "Hello World!");
app.Run();

Give feedback

We hope you enjoy this preview release of ASP.NET Core in .NET 6. We’re eager to hear about your experiences with this release. Let us know what you think by filing issues on GitHub.

Thanks for trying out ASP.NET Core!

Author

Daniel Roth
Principal Product Manager

Daniel Roth is a Program Manager on the ASP.NET team at Microsoft.

31 comments

Discussion is closed. Login to edit/delete existing comments.

  • Guruprasad K

    Expected more on Blazor.NET! disappointing to know about smaller releases!

  • Esben Sundgaard

    I love the new Minimal API and it is very nice that we can now use Swagger with it. However, when using url parameters Swagger doesn’t seams to work when specifying type and/or min value.
    E.g. this does not work:

    <code>

    However, this is working:

    <code>

    Will this be fixed later on or is there some way to get it working already?

    Read more
  • Diana Boone

    Dan, My question is about speeding up deserializing xml from an api request in blazor webassembly. Will AOT in .net 6 in November be able to speed up deserialization? I’ve tried AOT with .net 6 preview and I’m receiving an error that I reported on GitHub.

    • Daniel RothMicrosoft employee Author

      Hi Diana. Could you please share with me the link to the GitHub issue you opened? AOT should speed up CPU intensive tasks in general, but I don’t think we’ve done any specific perf testing with XML deserialization.

  • Sean G. Wright

    Thank you for continuing to invest in server-side Razor with View Components and Tag Helpers 🤘🤘

  • Oleg Savelos

    That is great news!

    Trying to add the WebView (WPF) to a newly created project and for some reason it fails

    Error NU1202 Package Microsoft.AspNetCore.Components.WebView.Wpf 6.0.100-preview.6.1003 is not compatible with net6.0-windows7.0 (.NETCoreApp,Version=v6.0). Package Microsoft.AspNetCore.Components.WebView.Wpf 6.0.100-preview.6.1003 supports: net6.0-windows10.0.19041 (.NETCoreApp,Version=v6.0)

    I do have latest SDK installed https://dotnet.microsoft.com/download/dotnet/thank-you/sdk-6.0.100-preview.6-windows-x64-installer
    Is this some kind of bug ?

    Thanks!

    Read more
    • Eilon LiptonMicrosoft employee

      Hi Oleg, that does appear to be a bug. I logged https://github.com/dotnet/maui/issues/1685 and we’ll get that fixed as soon as possible. In the meantime as a workaround you can update the of your project (CSPROJ file) to be net6.0-windows10.0.19041. This means your project for now will require Windows 10, until we fix the bug, and then you can undo that change. Thanks for the bug report!

      • Oleg Savelos

        Np! Keep up good work guys! Very excited to see this features 🙂

  • Howard Richards

    Dan the Preview 4 link under Open API support .. is broken.. I think it should be https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-preview-4/#introducing-minimal-apis

    You currently have https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-preview-6/evblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-preview-4/#introducing-minimal-apis

    Otherwise good work and thanks to the team..

    Looking forward to seeing the improvements to the Razor Editor in VS2022 Preview 2 and VS2019 16.11 Preview 3 .. that's my only real pain point with developing in Blazor right now..

    Read more
    • Daniel RothMicrosoft employee Author

      Hi Howard. Yup, the link should be fixed now. Thanks for letting us know!

      We hope the new Razor editor in VS2022 addresses the pain points you’ve been hitting. If you still run into issues, please report them using the VS feedback tool.

  • JinShil · Edited

    The 2 most desired features in ASP.NET 6 for me are Blazor AOT and Blazor Desktop. Unfortunately, AOT doesn't work on my target platforms due to this bug, and it appears Blazor Desktop has been taken out of scope for my target platforms.

    I suppose the AOT bug will be resolved eventually, but for Blazor Desktop on Linux I have the following suggestion:

    Anyone could create a WebView/WebWindow/WebKit control relatively easily by learning the public, well-documented...

    Read more
    • Eilon LiptonMicrosoft employee

      Hi JinShil, we do have a sample built on Photino.NET for testing purposes: https://github.com/dotnet/aspnetcore/tree/main/src/Components/WebView/Samples/PhotinoPlatform. You could use that as a starting point for any project you're working on. We'd love to support Linux in the future, but right now we are focusing on .NET MAUI for WinUI/MacCatalyst/iOS/Android and also WPF/WinForms. There should be enough public API surface for others to add their own platforms, just like the Photino.NET sample. If you feel there are any...

      Read more
  • Jason Chen · Edited

    So excited!

    EDITED: Sorry Dan, as I recreate the project from scratch to report the issue, the issue went away when I updated all the packages to preview 6. I didn't want to create an issue earlier because there was a number of dotnet watch issue on preview 5 or earlier. Looks like it's working now. Sorry for the confusion (part of the reason why I didn't want to create bugs on previews since you guys...

    Read more
    • Daniel RothMicrosoft employee Author

      Thanks Jason for letting us know! Could you please create a GitHub issue for this with some additional details on how to reproduce the problem?: https://github.com/dotnet/aspnetcore/issues/new. That way we can get the right folks on the engineering team involved to investigate.

      • Jason Chen

        Will do! Thanks again

  • Stilgar Naib

    Can someone explain why compression cannot be used with sensitive data?

    • Daniel RothMicrosoft employee Author

      Hi Stilgar. The concern is that compressed data over encrypted connections is susceptible to CRIME/BREACH attacks. I’ve updated the blog text to clarify this.

  • Rehan Saeed · Edited

    How do we annotate MapGet calls for Open API swagger generation? Say I want to add a description, add request/response examples and so on.

    Also, we can’t we use Activity.Current to get the activity for the current request, why do we need to call:

    var activity = context.Features.Get()?.Activity;
    • David FowlerMicrosoft employee

      You can use the same attributes (though some don’t work in this preview) you would in MVC.

    • Sourabh Shirhatti Microsoft employee

      Somebody (e.g., another middleware) may have created an intermediate activity in the causal chain of activities. In that case, Activity.Current won't the Activity associated with the request, but Activity.Current.Parent (or somewhere in the that hierarchical chain) will be.

      In practice, I expect user code to only use Activity.Current. I would only use the Activity from the feature to establish a contract with your listener. For example, if you wanted to implement the OpenTelemey HTTP semantic conventions,...

      Read more