-
Notifications
You must be signed in to change notification settings - Fork 24.7k
What's new in ASP.NET Core in .NET 11 Preview 6 #37334
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
d5de904
Initial plan
Copilot 9369471
Add What's New includes for ASP.NET Core .NET 11 Preview 6
Copilot e4523cc
Apply suggestions from code review
wadepickett 8fac435
Apply suggestions from code review
guardrex b6a96df
Apply suggestions from code review
guardrex 0c316f5
Merge remote-tracking branch 'origin/main' into copilot/update-whats-…
Copilot d145dc2
Give Blazor PR #37322 precedence in csharp-unions merge conflict
Copilot 60d5019
Add dotnet user-jwts file-based apps Preview 6 include
Copilot 7be2906
Apply suggestions from code review
wadepickett f08ee9c
Apply suggestions from code review
wadepickett 3fea3c4
Update async validation documentation for minimal APIs
wadepickett cfeb011
Apply suggestion from @wadepickett
wadepickett 7e4644f
Implement room availability check in validation
wadepickett 7f664b2
Update async-validation-minimal-apis-preview-6.md
wadepickett 206a4ec
Apply suggestion from @wadepickett
wadepickett File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
...release-notes/aspnetcore-11/includes/async-validation-minimal-apis-preview-6.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| ### Async validation for minimal APIs | ||
|
|
||
| Minimal API validation now supports asynchronous validators end-to-end ([dotnet/aspnetcore #66487](https://github.com/dotnet/aspnetcore/pull/66487), [dotnet/aspnetcore #67183](https://github.com/dotnet/aspnetcore/pull/67183)). Preview 5 shipped the building blocks for asynchronous form validation in Blazor. Preview 6 adds new asynchronous `DataAnnotations` APIs in the base libraries (`AsyncValidationAttribute` and `IAsyncValidatableObject`), and `Microsoft.Extensions.Validation` now runs them when an endpoint validates a request. | ||
|
|
||
| The simplest way to add an asynchronous rule is a custom validation attribute. Derive from `AsyncValidationAttribute` and implement `IsValidAsync` to query a database or call a remote API without blocking a thread. The synchronous `IsValid` is abstract too; throw from it when the attribute validates asynchronously only: | ||
|
|
||
| ```csharp | ||
| using System.ComponentModel.DataAnnotations; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
|
|
||
| public sealed class UniqueEmailAttribute : AsyncValidationAttribute | ||
| { | ||
| // Synchronous IsValid. This attribute validates asynchronously only. | ||
| protected override ValidationResult? IsValid(object? value, ValidationContext context) => | ||
| throw new InvalidOperationException("Validate this attribute with IsValidAsync."); | ||
|
|
||
| protected override async Task<ValidationResult?> IsValidAsync( | ||
| object? value, ValidationContext context, CancellationToken cancellationToken) | ||
| { | ||
| var users = context.GetRequiredService<IUserService>(); | ||
|
|
||
| if (value is string email && await users.EmailExistsAsync(email, cancellationToken)) | ||
| { | ||
| return new ValidationResult("That email is already registered."); | ||
| } | ||
|
|
||
| return ValidationResult.Success; | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Apply `[UniqueEmail]` to a property like any built-in validation attribute. | ||
|
|
||
| For validation that spans several properties or the whole object, implement `IAsyncValidatableObject` and return results as an `IAsyncEnumerable<ValidationResult>`. Because `IAsyncValidatableObject` extends `IValidatableObject`, also implement the synchronous `Validate` method. When a type validates asynchronously only, throw from `Validate` so its validation isn't silently skipped by the synchronous APIs: | ||
|
|
||
| ```csharp | ||
| using System.ComponentModel.DataAnnotations; | ||
| using System.Runtime.CompilerServices; | ||
|
|
||
| public class ReservationRequest : IAsyncValidatableObject | ||
| { | ||
| [Required] | ||
| public string Email { get; set; } = ""; | ||
|
|
||
| public DateOnly Date { get; set; } | ||
|
|
||
| // Synchronous IValidatableObject. This type validates asynchronously only. | ||
| public IEnumerable<ValidationResult> Validate(ValidationContext context) => | ||
| throw new InvalidOperationException("Validate this type with ValidateAsync."); | ||
|
|
||
| public async IAsyncEnumerable<ValidationResult> ValidateAsync( | ||
| ValidationContext context, | ||
| [EnumeratorCancellation] CancellationToken cancellationToken = default) | ||
| { | ||
| var rooms = context.GetRequiredService<IRoomService>(); | ||
|
|
||
| if (!await rooms.HasAvailabilityAsync(Date, cancellationToken)) | ||
|
guardrex marked this conversation as resolved.
|
||
| { | ||
| yield return new ValidationResult( | ||
| "No rooms are available on that date.", [nameof(Date)]); | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Register validation and the framework validates the request before the endpoint runs: | ||
|
|
||
| ```csharp | ||
| builder.Services.AddValidation(); | ||
|
|
||
| app.MapPost("/reservations", (ReservationRequest request) => | ||
| Results.Ok(request)); | ||
| ``` | ||
|
|
||
| Validators run concurrently where possible: asynchronous attributes on the same member start together, collection items validate in parallel, and the framework preserves the existing ordering between member, type, and `IValidatableObject` validation. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
aspnetcore/release-notes/aspnetcore-11/includes/openapi-3-2-default-preview-6.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| ### OpenAPI 3.2 by default | ||
|
|
||
| Generated OpenAPI documents now target OpenAPI 3.2 by default. Documents continue to generate as before. Set the document version explicitly if you need to target an earlier version for tooling that doesn't yet support OpenAPI 3.2. | ||
|
|
||
| To target an earlier version, specify it when calling <xref:Microsoft.Extensions.DependencyInjection.OpenApiServiceCollectionExtensions.AddOpenApi%2A>: | ||
|
|
||
| ```csharp | ||
| builder.Services.AddOpenApi(options => | ||
| { | ||
| options.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_1; | ||
| }); | ||
| ``` |
26 changes: 26 additions & 0 deletions
26
...ase-notes/aspnetcore-11/includes/short-circuit-endpoints-attribute-preview-6.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| ### Short-circuit endpoints with an attribute | ||
|
|
||
| The new `[ShortCircuit]` attribute marks an endpoint to run immediately after routing, skipping the rest of the middleware pipeline. This is the attribute form of the existing `ShortCircuit()` endpoint convention, so it can be applied directly to MVC controllers and actions. | ||
|
|
||
| <!-- TODO: Update `[ShortCircuit]` to <xref:> once API docs are published. --> | ||
|
|
||
| Short-circuiting is useful for endpoints that don't need authentication, CORS, or other middleware—for example a health check or a `robots.txt` response, and it avoids the cost of running that middleware. The endpoint still runs and produces its response. Pass an optional status code, such as `[ShortCircuit(404)]`, to set the response status code. | ||
|
|
||
| ```csharp | ||
| [ApiController] | ||
| [Route("robots.txt")] | ||
| [ShortCircuit] | ||
| public class RobotsController : ControllerBase | ||
| { | ||
| [HttpGet] | ||
| public IActionResult Get() => Content("User-agent: *\nDisallow:", "text/plain"); | ||
| } | ||
| ``` | ||
|
|
||
| The same attribute works on minimal API endpoints, and the existing `ShortCircuit()` convention continues to work unchanged: | ||
|
|
||
| ```csharp | ||
| app.MapGet("/health", [ShortCircuit] () => "Healthy"); | ||
| ``` | ||
|
|
||
| Thank you [@Porozhniakov](https://github.com/Porozhniakov) for contributing this feature! |
45 changes: 45 additions & 0 deletions
45
...elease-notes/aspnetcore-11/includes/signalr-authentication-refresh-preview-6.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| ### SignalR authentication refresh | ||
|
|
||
| SignalR connections can refresh authentication without dropping the connection when the access token expires. The server exposes a `/refresh` endpoint alongside `/negotiate` and reports the token lifetime in the negotiate response. The .NET client re-authenticates before the token expires, so a hub connection that previously closed when its bearer token aged out can stay open. This feature is implemented for the .NET client; the JavaScript/TypeScript client and Azure SignalR Service support are in progress. | ||
|
|
||
| <!-- TODO: Update `EnableAuthenticationRefresh`, `OnAuthenticationRefresh`, `OnAuthenticationRefreshedAsync`, and `WithAuthenticationRefresh` to <xref:> once API docs are published. --> | ||
|
|
||
| Enable the feature per hub on the server: | ||
|
|
||
| ```csharp | ||
| app.MapHub<ChatHub>("/chat", options => | ||
| { | ||
| options.EnableAuthenticationRefresh = true; | ||
|
|
||
| // Optional: decide whether a given connection can refresh. | ||
| options.OnAuthenticationRefresh = context => ValueTask.FromResult(true); | ||
| }); | ||
| ``` | ||
|
|
||
| A hub can react to a refreshed identity by overriding `OnAuthenticationRefreshedAsync`: | ||
|
|
||
| ```csharp | ||
| public class ChatHub : Hub | ||
| { | ||
| public override Task OnAuthenticationRefreshedAsync() | ||
| { | ||
| // The connection's User has been updated with the refreshed token. | ||
| return Task.CompletedTask; | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Automatic refresh is on by default in the .NET client and is configurable with `WithAuthenticationRefresh`: | ||
|
|
||
| ```csharp | ||
| var connection = new HubConnectionBuilder() | ||
| .WithUrl("https://example.com/chat") | ||
| .WithAuthenticationRefresh(options => | ||
| { | ||
| // EnableAutoRefresh is true by default. | ||
| options.RefreshBeforeExpiration = TimeSpan.FromMinutes(1); | ||
| options.OnAuthenticationRefreshed = context => Task.CompletedTask; | ||
| options.OnAuthenticationRefreshFailed = context => Task.CompletedTask; | ||
| }) | ||
| .Build(); | ||
| ``` |
22 changes: 22 additions & 0 deletions
22
...elease-notes/aspnetcore-11/includes/signalr-cancel-hub-invocations-preview-6.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| ### Cancel hub invocations from the client | ||
|
|
||
| The SignalR client can cancel a regular, non-streaming hub method invocation. Previously only streaming invocations could be canceled from the client. Now, when you pass a <xref:System.Threading.CancellationToken> to <xref:Microsoft.AspNetCore.SignalR.Client.HubConnectionExtensions.InvokeAsync%2A> and cancel it, the client sends a cancellation message, and the hub method's `CancellationToken` parameter is triggered on the server. | ||
|
|
||
| ```csharp | ||
| // Client — canceling the token cancels the server-side invocation. | ||
| using var cts = new CancellationTokenSource(); | ||
| var work = connection.InvokeAsync("LongRunningWork", cts.Token); | ||
| // ... | ||
| cts.Cancel(); | ||
| ``` | ||
|
|
||
| ```csharp | ||
| // Hub — accept a CancellationToken to observe client cancellation. | ||
| public class WorkHub : Hub | ||
| { | ||
| public async Task LongRunningWork(CancellationToken cancellationToken) | ||
| { | ||
| await Task.Delay(TimeSpan.FromMinutes(5), cancellationToken); | ||
| } | ||
| } | ||
| ``` |
7 changes: 7 additions & 0 deletions
7
...ore/release-notes/aspnetcore-11/includes/user-jwts-file-based-apps-preview-6.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| ### `dotnet user-jwts` supports file-based apps | ||
|
|
||
| The `dotnet user-jwts` tool creates signed development JWTs so you can call an app's authenticated endpoints without setting up a real identity provider. The `create` command generates a token, stores its signing key in the app's user secrets, and prints the token to use as a bearer token. It now works with file-based apps (a single `app.cs` with no project file) through the new `--file` option: | ||
|
|
||
| ```bash | ||
| dotnet user-jwts create --file app.cs | ||
| ``` |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.