.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 removedrole="alert"
from the survey prompt component to deemphasize its content. - We updated the
Counter
component to addrole="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
andarticle
. - We swapped out the
ul
in theNavBar
component for anav
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 theNavBar
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:
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.
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.
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!
The post ASP.NET Core updates in .NET 6 Preview 6 appeared first on ASP.NET Blog.
Comments
Post a Comment