[release-notes] ASP.NET Core in .NET 11 Preview 5#10422
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Hands-on testing with the samples app surfaced two Preview 5 ASP.NET Core features that weren't in the first draft: async form validation (dotnet/aspnetcore #66526) and resource-based validation localization (dotnet/aspnetcore #66646). Both ship together and are foundational for the SSR client validation section, so add dedicated sections covering the EditContext.AddValidationTask / IsValidationPending API and the AddValidationLocalization<T> wire-up. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Found via a milestone cross-check sweep against the draft: PR #65650 was merged into main on 2026-05-14 with labels area-mvc,feature-openapi but no 11.0-preview5 milestone tag, which kept it off the editorial scoring pass's radar even though it changes user-visible MVC + minimal API + OpenAPI behavior. Adds a dedicated section under the OpenAPI cluster. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@davpetr FYI: Generated ASP.NET Core release notes for .NET 11 Preview 5. |
There was a problem hiding this comment.
Pull request overview
This PR adds ASP.NET Core component release notes for .NET 11 Preview 5, fitting into the preview milestone documentation set that rolls up into the umbrella release-notes PR.
Changes:
- Adds feature highlights for Blazor, QuickGrid, Kestrel, and OpenAPI updates.
- Adds bug-fix summaries grouped by ASP.NET Core area.
- Adds community contributor acknowledgements for the milestone.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Daniel Roth <daroth@microsoft.com>
mikekistler
left a comment
There was a problem hiding this comment.
Looks good with my one suggested change. 👍
There was a problem hiding this comment.
I posted the re-worked sections as regular comments to avoid problems with code blocks inside suggestion blocks.
Feel free to make the sections shorter if there is too much detail. However, the original texts were confusing and leaking implementation details.
| - [Blazor SSR forms validate in the browser](#blazor-ssr-forms-validate-in-the-browser) | ||
| - [Blazor forms support async validation](#blazor-forms-support-async-validation) | ||
| - [Validation supports resource-based localization](#validation-supports-resource-based-localization) |
There was a problem hiding this comment.
| - [Blazor SSR forms validate in the browser](#blazor-ssr-forms-validate-in-the-browser) | |
| - [Blazor forms support async validation](#blazor-forms-support-async-validation) | |
| - [Validation supports resource-based localization](#validation-supports-resource-based-localization) | |
| - [Blazor SSR supports client-side validation](#blazor-ssr-supports-client-side-validation) | |
| - [Blazor supports async form validation](#blazor-supports-async-form-validation) | |
| - [Blazor and Minimal APIs support localized validation errors](#blazor-and-minimal-apis-support-error-localization) |
| ## Validation supports resource-based localization | ||
|
|
||
| `Microsoft.Extensions.Validation` now ships with built-in localization that flows through Blazor's `DataAnnotationsValidator` and minimal APIs that opt into the new validation pipeline ([dotnet/aspnetcore #66646](https://github.com/dotnet/aspnetcore/pull/66646)). `AddValidationLocalization<TResource>()` registers an `IValidationLocalizer` that resolves `[Display(Name = ...)]` values and `ValidationAttribute.ErrorMessage` keys against an `IStringLocalizer<TResource>` backed by `.resx` files. The same model produces the same display names and validation messages on the server, in the rendered HTML for client-side validation, and in localized API error responses. | ||
|
|
||
| ```csharp | ||
| builder.Services.AddLocalization(); | ||
| builder.Services.AddValidation(); | ||
| builder.Services.AddValidationLocalization<ValidationMessages>(); | ||
| ``` | ||
|
|
||
| ```csharp | ||
| [ValidatableType] | ||
| public class ContactModel | ||
| { | ||
| [Required(ErrorMessage = nameof(ValidationMessages.RequiredError))] | ||
| [EmailAddress(ErrorMessage = nameof(ValidationMessages.EmailError))] | ||
| [Display(Name = nameof(ValidationMessages.ContactEmail))] | ||
| public string? Email { get; set; } | ||
| } | ||
| ``` |
There was a problem hiding this comment.
Blazor and Minimal APIs support error localization
Validation of Blazor forms and Minimal API endpoints gets first-class support for localization of error messages and property names. By default, localization uses language-specific RESX files deployed as part of the assembly.
builder.Services.AddValidation()
.AddValidationLocalization<ValidationMessages>();
// Resolves to ValidationMessages.en.resx, ValidationMessages.es.resx, ...[ValidatableType]
public class ContactModel
{
// Values of ErrorMessage are used as localization keys.
[Required(ErrorMessage = "RequiredError")]
[EmailAddress(ErrorMessage = "EmailError")]
[Display(Name = "ContactEmail")]
public string? Email { get; set; }
}Applications can also register custom IStringLocalizerFactory implementations to read the localized strings from other sources such as databases or JSON files. User registered type takes precedence over the default RESX localization.
builder.Services.AddValidation()
.AddValidationLocalization();
builder.Services.AddSingleton<IStringLocalizerFactory, DbStringLocalizerFactory>();Applications can also configure a programmatic strategy for localization, removing the need to specify localization keys on every validation attribute.
builder.Services.AddValidation()
.AddValidationLocalization<ValidationMessages>(options =>
{
options.ErrorMessageKeyProvider = ctx =>
ctx.Attribute.ErrorMessage ?? $"{ctx.Attribute.GetType().Name}_Error";
});[ValidatableType]
public class ContactModel
{
// Looks-up localized string for 'RequiredAttribute_Error' automatically.
[Required]
public string? Username { get; set; }
}| ## Blazor forms support async validation | ||
|
|
||
| Blazor forms now support asynchronous validation rules without leaving the `EditContext` model ([dotnet/aspnetcore #66526](https://github.com/dotnet/aspnetcore/pull/66526)). `EditContext.AddValidationTask(FieldIdentifier, Task, CancellationTokenSource)` registers an in-flight check that the framework tracks per field, cancels when superseded, and surfaces through new `IsValidationPending`, `IsValidationFaulted`, and `IsValidationSuperseded` queries. `OnValidationRequestedAsync` lets submit handlers await the same pipeline before reporting overall validity, so async uniqueness, server lookups, and remote calls participate in the standard `ValidationMessageStore`/`ValidationSummary` flow. | ||
|
|
||
| ```razor | ||
| <EditForm EditContext="editContext" OnSubmit="HandleSubmit"> | ||
| <InputText @bind-Value="model.Username" /> | ||
| @if (editContext.IsValidationPending(() => model.Username)) | ||
| { | ||
| <span>Checking availability...</span> | ||
| } | ||
| <ValidationMessage For="() => model.Username" /> | ||
| <button type="submit">Register</button> | ||
| </EditForm> | ||
|
|
||
| @code { | ||
| [Inject] public UserService Users { get; set; } = default!; | ||
|
|
||
| private readonly RegistrationModel model = new(); | ||
| private EditContext editContext = default!; | ||
| private ValidationMessageStore messages = default!; | ||
|
|
||
| protected override void OnInitialized() | ||
| { | ||
| editContext = new EditContext(model); | ||
| messages = new ValidationMessageStore(editContext); | ||
| editContext.OnFieldChanged += (_, e) => | ||
| { | ||
| if (e.FieldIdentifier.FieldName == nameof(model.Username)) | ||
| { | ||
| var cts = new CancellationTokenSource(); | ||
| editContext.AddValidationTask(e.FieldIdentifier, | ||
| CheckAsync(e.FieldIdentifier, model.Username, cts.Token), cts); | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| private async Task CheckAsync(FieldIdentifier field, string value, CancellationToken ct) | ||
| { | ||
| messages.Clear(field); | ||
| if (await Users.IsUsernameTakenAsync(value, ct)) | ||
| { | ||
| messages.Add(field, "Username is taken."); | ||
| } | ||
| editContext.NotifyValidationStateChanged(); | ||
| } | ||
|
|
||
| private async Task HandleSubmit() => await editContext.ValidateAsync(); | ||
| } | ||
| ``` |
There was a problem hiding this comment.
Blazor supports async form validation
Blazor forms get support for async validation rules such as database lookups or remote API calls. In any rendering mode, EditForm submit validation now properly awaits async validators end-to-end. In interactive modes, validator components can register per-field async tasks via EditContext.AddValidationTask The framework tracks them, cancels superseded tasks, and exposes progress status via IsValidationPending(field) and IsValidationFaulted(field).
While Preview 5 ships the building blocks for Blazor forms, the full built-in async validation experience will be enabled when the new asynchronous DataAnnotations APIs are released in a later Preview. These APIs will be fully supported by the existing DataAnnotationsValidator component.
<EditForm EditContext="editContext" OnSubmit="HandleSubmit">
<InputText @bind-Value="model.Username" />
@if (editContext.IsValidationPending(() => model.Username))
{
<span>Checking availability...</span>
}
<ValidationMessage For="() => model.Username" />
<button type="submit">Register</button>
</EditForm>
@code {
[Inject] public UserService Users { get; set; } = default!;
private readonly RegistrationModel model = new();
private EditContext editContext = default!;
private ValidationMessageStore messages = default!;
protected override void OnInitialized()
{
editContext = new EditContext(model);
messages = new ValidationMessageStore(editContext);
editContext.OnFieldChanged += (_, e) =>
{
if (e.FieldIdentifier.FieldName == nameof(model.Username))
{
var cts = new CancellationTokenSource();
editContext.AddValidationTask(e.FieldIdentifier,
CheckAsync(e.FieldIdentifier, model.Username, cts.Token), cts);
}
};
}
private async Task CheckAsync(FieldIdentifier field, string value, CancellationToken ct)
{
messages.Clear(field);
if (await Users.IsUsernameTakenAsync(value, ct))
{
messages.Add(field, "Username is taken.");
}
editContext.NotifyValidationStateChanged();
}
private async Task HandleSubmit() => await editContext.ValidateAsync();
}| ## Blazor SSR forms validate in the browser | ||
|
|
||
| Blazor SSR forms now render client-side validation metadata from `System.ComponentModel.DataAnnotations` attributes and include a small JavaScript validator that reads the generated `data-val-*` attributes ([dotnet/aspnetcore #66441](https://github.com/dotnet/aspnetcore/pull/66441), [dotnet/aspnetcore #66420](https://github.com/dotnet/aspnetcore/pull/66420)). Static SSR forms that already use `DataAnnotationsValidator` can show validation messages before posting back to the server. The .NET model remains the source of truth for validation rules, and the browser gets the same instant feedback pattern used by MVC unobtrusive validation. | ||
|
|
||
| ```razor | ||
| @page "/subscribe" | ||
| @using System.ComponentModel.DataAnnotations | ||
|
|
||
| <EditForm Model="Model" FormName="subscribe" OnValidSubmit="SubscribeAsync"> | ||
| <DataAnnotationsValidator /> | ||
|
|
||
| <label> | ||
| <InputText @bind-Value="Model.Email" /> | ||
| </label> | ||
| <ValidationMessage For="() => Model.Email" /> | ||
|
|
||
| <button type="submit">Subscribe</button> | ||
| </EditForm> | ||
|
|
||
| @code { | ||
| private SubscribeForm Model { get; } = new(); | ||
|
|
||
| private Task SubscribeAsync() => Task.CompletedTask; | ||
|
|
||
| private sealed class SubscribeForm | ||
| { | ||
| [Required] | ||
| [EmailAddress] | ||
| public string? Email { get; set; } | ||
| } | ||
| } | ||
| ``` |
There was a problem hiding this comment.
Blazor SSR supports client-side validation
Blazor SSR forms now get instant, in-browser validation feedback without a server round-trip, matching the experience provided by interactive Blazor apps and MVC apps with unobtrusive validation. The .NET model remains the single source of truth for validation rules. The server renders metadata for the validation rules which are then enforced by the Blazor JS code on the client-side.
The feature is enabled by default for all SSR forms that include the DataAnnotationsValidator component. Both enhanced and non-enhanced forms are supported.
<EditForm Model="Model" Enhance FormName="registration" OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator />
<div>
<label for="Email">Email</label>
<InputText @bind-Value="Model.Email" id="Email" />
<ValidationMessage For="@(() => Model.Email)" />
</div>
<div>
<label for="Password">Password</label>
<InputText @bind-Value="Model.Password" id="Password" type="password" />
<ValidationMessage For="@(() => Model.Password)" />
</div>
<div>
<label for="ConfirmPassword">Confirm Password</label>
<InputText @bind-Value="Model.ConfirmPassword" id="ConfirmPassword" type="password" />
<ValidationMessage For="@(() => Model.ConfirmPassword)" />
</div>
<div>
<button type="submit" id="submit-btn">Register</button>
</div>
</EditForm>public class RegistrationModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[StringLength(100, MinimumLength = 8)]
public string Password { get; set; }
[Required]
[Compare("Password")]
[Display(Name = "Confirm Password")]
public string ConfirmPassword { get; set; }
}- Rewrite Blazor validation sections (SSR client-side, async, localization) per @oroztocil's suggestions - Update TOC entries to match new section titles - Rewrite #65650 OpenAPI paragraph per @mikekistler's suggestion (and drop the leaked vscode-file URL) - Remove @cincuranet from community contributors per @Youssef1313 (not a community contributor) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ASP.NET Core release notes for .NET 11 Preview 5.
This component PR targets the base milestone branch
release-notes/11.0-preview5(umbrella PR: #10421). Review and edit the markdown here in isolation — when this PR merges into the base branch, the changes roll up into the umbrella PR.Draft generated with the
release-notesskill in this repo. Verify feature selection, code samples, API names, and contributor attributions before marking ready for review.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com