ASP.NET Core 7.0 的新功能

本文會重點說明 ASP.NET Core 7.0 中最重要的變更,並附上相關文件的連結。

ASP.NET Core 中的速率限制中介軟體

Microsoft.AspNetCore.RateLimiting 中介軟體提供速率限制中介軟體。 應用程式會設定速率限制原則,然後將原則附加至端點。 如需詳細資訊,請參閱 ASP.NET Core 中的速率限制中介軟體

驗證是以單一配置作為 DefaultScheme

在簡化驗證的工作中,當只註冊單一驗證配置時,就會自動將其作為 DefaultScheme 而不需進行指定。 如需詳細資訊,請參閱 DefaultScheme

MVC 和 Razor Pages

支援 MVC 檢視和 Razor Pages 中可為 Null 的模型

對於可為 Null 的頁面或檢視表模型的支援,可改善 ASP.NET Core 應用程式搭配使用 Null 狀態檢查時的體驗:

@model Product?

在 MVC 和 API 控制器中繫結 IParsable<T>.TryParse

IParsable<TSelf>.TryParse API 可支援繫結控制器動作參數值。 如需詳細資訊,請參閱繫結 IParsable<T>.TryParse

在版本 7 之前的 ASP.NET Core 中,cookie 同意驗證是使用 cookie 值 yes 來表示同意。 現在您可以指定代表同意的值。 例如,您可以使用 true 而非 yes

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.Configure<CookiePolicyOptions>(options =>
{
    options.CheckConsentNeeded = context => true;
    options.MinimumSameSitePolicy = SameSiteMode.None;
    options.ConsentCookieValue = "true";
});

var app = builder.Build();

如需詳細資訊,請參閱自訂 cookie 同意值

API 控制器

在 API 控制器中使用 DI 的參數繫結

將類型設定為服務時,API 控制器動作的參數繫結會透過相依性插入來繫結參數。 這表示不再需要明確將 [FromServices] 屬性套用至參數。 在下列程式碼中,這兩個動作都會傳回時間:

[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
    public ActionResult GetWithAttribute([FromServices] IDateTime dateTime) 
                                                        => Ok(dateTime.Now);

    [Route("noAttribute")]
    public ActionResult Get(IDateTime dateTime) => Ok(dateTime.Now);
}

在極少數情況下,自動 DI 可能會中斷 DI 類型也被 API 控制器動作方法接受的應用程式。 在 DI 擁有類型並作為 API 控制器動作中的引數並不常見。 若要停用參數的自動繫結,請設定 DisableImplicitFromServicesParameters

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddSingleton<IDateTime, SystemDateTime>();

builder.Services.Configure<ApiBehaviorOptions>(options =>
{
    options.DisableImplicitFromServicesParameters = true;
});

var app = builder.Build();

app.MapControllers();

app.Run();

在 ASP.NET Core 7.0 中,應用程式啟動時會透過 IServiceProviderIsService 檢查 DI 中的類型,以判斷 API 控制器動作中的引數是來自 DI 抑或其他來源。

可供推斷 API 控制器動作參數繫結來源的新機制會使用下列規則:

  1. 先前指定的 BindingInfo.BindingSource 永遠不會被覆寫。
  2. 在 DI 容器中註冊的複雜類型參數會被指派 BindingSource.Services
  3. 未在 DI 容器中註冊的複雜類型參數會被指派 BindingSource.Body
  4. 任何路由範本中名稱呈現為路由值的參數會被指派 BindingSource.Path
  5. 所有其他參數都是 BindingSource.Query

驗證錯誤中的 JSON 屬性名稱

根據預設,當驗證錯誤發生時,模型驗證會產生屬性名稱做為錯誤索引鍵的 ModelStateDictionary。 某些應用程式 (例如單頁應用程式) 受益於使用 JSON 屬性名稱來取得 Web API 所產生的驗證錯誤。 下列程式碼會設定驗證以透過 SystemTextJsonValidationMetadataProvider 來使用 JSON 屬性名稱:

using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
    options.ModelMetadataDetailsProviders.Add(new SystemTextJsonValidationMetadataProvider());
});

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

下列程式碼會設定驗證,以在使用 Json.NET 時,透過 NewtonsoftJsonValidationMetadataProvider 來使用 JSON 屬性名稱:

using Microsoft.AspNetCore.Mvc.NewtonsoftJson;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
    options.ModelMetadataDetailsProviders.Add(new NewtonsoftJsonValidationMetadataProvider());
}).AddNewtonsoftJson();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

如需詳細資訊,請參閱 在驗證錯誤中使用 JSON 屬性名稱

最小 API

最小 API 應用程式中的篩選

最小 API 篩選條件可讓開發人員實作相關商務邏輯,以支援:

  • 在路由處理常式前後執行程式碼。
  • 檢查和修改路由處理常式調用期間所提供的參數。
  • 攔截路由處理常式的回應行為。

篩選條件在下列情況下很有用:

  • 驗證傳送至端點的要求參數和本文。
  • 記錄要求和回應的相關資訊。
  • 驗證要求是以支援的 API 版本為目標。

如需詳細資訊,請參閱最小 API 應用程式中的篩選條件

從標頭和查詢字串繫結陣列和字串值

在 ASP.NET 7 中,支援將查詢字串繫結至基本類型、字串陣列和 StringValues 三者的陣列:

// Bind query string values to a primitive type array.
// GET  /tags?q=1&q=2&q=3
app.MapGet("/tags", (int[] q) =>
                      $"tag1: {q[0]} , tag2: {q[1]}, tag3: {q[2]}");

// Bind to a string array.
// GET /tags2?names=john&names=jack&names=jane
app.MapGet("/tags2", (string[] names) =>
            $"tag1: {names[0]} , tag2: {names[1]}, tag3: {names[2]}");

// Bind to StringValues.
// GET /tags3?names=john&names=jack&names=jane
app.MapGet("/tags3", (StringValues names) =>
            $"tag1: {names[0]} , tag2: {names[1]}, tag3: {names[2]}");

型別已實作 TryParse 時,支援將查詢字串或標頭值繫結至複雜型別的陣列。 如需詳細資訊,請參閱從標頭和查詢字串繫結陣列和字串值

如需詳細資訊,請參閱新增端點摘要或描述

將要求本文繫結為 StreamPipeReader

要求本文可以繫結為 StreamPipeReader,以有效率地支援使用者必須處理資料的案例,並:

  • 將資料儲存至 Blob 儲存體,或將資料加入佇列提供者的佇列。
  • 使用背景工作處理序或雲端函式處理儲存的資料。

例如,資料可能會加入佇列至 Azure 佇列儲存體,或儲存在 Azure Blob 儲存體中。

如需詳細資訊,請參閱 將要求本文繫結為 StreamPipeReader

新的 Results.Stream 多載

我們引進了新的 Results.Stream 多載,以容納需要存取基礎 HTTP 回應資料流而不需緩衝處理的案例。 這些多載也會改善 API 將資料串流至 HTTP 回應資料流 (例如從 Azure Blob 儲存體) 的情況。 下列範例會使用 ImageSharp 傳回縮小尺寸的指定影像:

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Processing;

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapGet("/process-image/{strImage}", (string strImage, HttpContext http, CancellationToken token) =>
{
    http.Response.Headers.CacheControl = $"public,max-age={TimeSpan.FromHours(24).TotalSeconds}";
    return Results.Stream(stream => ResizeImageAsync(strImage, stream, token), "image/jpeg");
});

async Task ResizeImageAsync(string strImage, Stream stream, CancellationToken token)
{
    var strPath = $"wwwroot/img/{strImage}";
    using var image = await Image.LoadAsync(strPath, token);
    int width = image.Width / 2;
    int height = image.Height / 2;
    image.Mutate(x =>x.Resize(width, height));
    await image.SaveAsync(stream, JpegFormat.Instance, cancellationToken: token);
}

如需詳細資訊,請參閱 資料流範例

最小 API 的具型別結果

.NET 6 中引進了 IResult 介面來表示從最小 API 傳回的值,此類 API 不會利用可將傳回物件序列化為 HTTP 回應的 JSON 隱式支援。 靜態 Results 類別是用來建立代表不同回應型別的不同 IResult 物件。 例如,設定回應狀態碼或重新導向至另一個 URL。 不過,從這些方法傳回的 IResult 實作架構類型屬內部性質,因此很難驗證從單元測試中的方法傳回的特定 IResult 類型。

在 .NET 7 中,實作 IResult 的類型則為公用性,可在測試時允許類型判斷提示。 例如:

[TestClass()]
public class WeatherApiTests
{
    [TestMethod()]
    public void MapWeatherApiTest()
    {
        var result = WeatherApi.GetAllWeathers();
        Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
    }      
}

改善最小路由處理常式的單元可測試性

IResult 實作類型現在可在 Microsoft.AspNetCore.Http.HttpResults 命名空間中公開使用。 IResult 實作類型可用來在使用具名方法而非 Lambda 時,對最小路由處理常式進行單元測試。

下列程式碼會使用 Ok<TValue> 類別:

[Fact]
public async Task GetTodoReturnsTodoFromDatabase()
{
    // Arrange
    await using var context = new MockDb().CreateDbContext();

    context.Todos.Add(new Todo
    {
        Id = 1,
        Title = "Test title",
        Description = "Test description",
        IsDone = false
    });

    await context.SaveChangesAsync();

    // Act
    var result = await TodoEndpointsV1.GetTodo(1, context);

    //Assert
    Assert.IsType<Results<Ok<Todo>, NotFound>>(result);

    var okResult = (Ok<Todo>)result.Result;

    Assert.NotNull(okResult.Value);
    Assert.Equal(1, okResult.Value.Id);
}

如需詳細資訊,請參閱 IResult 實作類型

新的 HttpResult 介面

下列 Microsoft.AspNetCore.Http 命名空間中的介面提供在執行階段偵測 IResult 型別的方法,這是篩選實作中的常見模式:

如需詳細資訊,請參閱 IHttpResult 介面

最小 API 的 OpenAPI 改善

Microsoft.AspNetCore.OpenApi NuGet 套件

Microsoft.AspNetCore.OpenApi 套件允許與端點的 OpenAPI 規格互動。 此套件會作為 Microsoft.AspNetCore.OpenApi 套件中所定義 OpenAPI 模型與最小 API 中所定義端點兩者間的連結。 此套件會提供 API 來檢查端點的參數、回應和中繼資料,以建構用來描述端點的 OpenAPI 註釋類型。

app.MapPost("/todoitems/{id}", async (int id, Todo todo, TodoDb db) =>
{
    todo.Id = id;
    db.Todos.Add(todo);
    await db.SaveChangesAsync();

    return Results.Created($"/todoitems/{todo.Id}", todo);
})
.WithOpenApi();

使用參數呼叫 WithOpenApi

WithOpenApi 方法會接受可用來修改 OpenAPI 註釋的函式。 例如,在下列程式碼中,會新增描述至端點的第一個參數:

app.MapPost("/todo2/{id}", async (int id, Todo todo, TodoDb db) =>
{
    todo.Id = id;
    db.Todos.Add(todo);
    await db.SaveChangesAsync();

    return Results.Created($"/todoitems/{todo.Id}", todo);
})
.WithOpenApi(generatedOperation =>
{
    var parameter = generatedOperation.Parameters[0];
    parameter.Description = "The ID associated with the created Todo";
    return generatedOperation;
});

提供端點描述和摘要

最小 API 現在支援使用 OpenAPI 規格產生作業的描述和摘要來批註作業。 您可以呼叫擴充方法 WithDescriptionWithSummary,或使用屬性 [EndpointDescription][EndpointSummary]

如需詳細資訊,請參閱最小 API 應用程式中的 OpenAPI

使用 IFormFile 和 IFormFileCollection 上傳檔案

最小 API 現在支援使用 IFormFileIFormFileCollection 上傳檔案。 下列程式碼會使用 IFormFileIFormFileCollection 來上傳檔案:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.MapPost("/upload", async (IFormFile file) =>
{
    var tempFile = Path.GetTempFileName();
    app.Logger.LogInformation(tempFile);
    using var stream = File.OpenWrite(tempFile);
    await file.CopyToAsync(stream);
});

app.MapPost("/upload_many", async (IFormFileCollection myFiles) =>
{
    foreach (var file in myFiles)
    {
        var tempFile = Path.GetTempFileName();
        app.Logger.LogInformation(tempFile);
        using var stream = File.OpenWrite(tempFile);
        await file.CopyToAsync(stream);
    }
});

app.Run();

支援使用授權標頭用戶端憑證或 cookie 標頭的已驗證檔案上傳要求。

其中並未內建防偽支援。 不過,可以使用 IAntiforgery 服務來實作此功能。

[AsParameters] 屬性會啟用引數清單的參數繫結

[AsParameters] 屬性會啟用引數清單的參數繫結。 如需詳細資訊,請參閱使用 [AsParameters] 進行引數清單的參數繫結

最小 API 和 API 控制器

新問題詳細資料服務

問題詳細資料服務會實作 IProblemDetailsService 介面,來支援建立 HTTP API 的問題詳細資料

如需詳細資訊,請參閱問題詳細資料服務

路由群組

MapGroup 擴充方法可協助組織具有常見前置詞的端點群組。 其可減少重複的程式碼,並允許使用單一呼叫方法 (例如 RequireAuthorizationWithMetadata,其可新增端點中繼資料) 來自訂整個端點群組。

例如,下列程式碼會建立兩個類似的端點群組:

app.MapGroup("/public/todos")
    .MapTodosApi()
    .WithTags("Public");

app.MapGroup("/private/todos")
    .MapTodosApi()
    .WithTags("Private")
    .AddEndpointFilterFactory(QueryPrivateTodos)
    .RequireAuthorization();


EndpointFilterDelegate QueryPrivateTodos(EndpointFilterFactoryContext factoryContext, EndpointFilterDelegate next)
{
    var dbContextIndex = -1;

    foreach (var argument in factoryContext.MethodInfo.GetParameters())
    {
        if (argument.ParameterType == typeof(TodoDb))
        {
            dbContextIndex = argument.Position;
            break;
        }
    }

    // Skip filter if the method doesn't have a TodoDb parameter.
    if (dbContextIndex < 0)
    {
        return next;
    }

    return async invocationContext =>
    {
        var dbContext = invocationContext.GetArgument<TodoDb>(dbContextIndex);
        dbContext.IsPrivate = true;

        try
        {
            return await next(invocationContext);
        }
        finally
        {
            // This should only be relevant if you're pooling or otherwise reusing the DbContext instance.
            dbContext.IsPrivate = false;
        }
    };
}
public static RouteGroupBuilder MapTodosApi(this RouteGroupBuilder group)
{
    group.MapGet("/", GetAllTodos);
    group.MapGet("/{id}", GetTodo);
    group.MapPost("/", CreateTodo);
    group.MapPut("/{id}", UpdateTodo);
    group.MapDelete("/{id}", DeleteTodo);

    return group;
}

在此案例中,您可以在 201 Created 結果中使用 Location 標頭的相對位址:

public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
    await database.AddAsync(todo);
    await database.SaveChangesAsync();

    return TypedResults.Created($"{todo.Id}", todo);
}

第一組端點只會比對以 /public/todos 為前置詞且不需要任何驗證即可存取的要求。 第二組端點只會比對以 /private/todos 為前置詞且需要驗證的要求。

QueryPrivateTodos端點篩選處理站是本機函式,可修改路由處理常式的 TodoDb 參數,以允許存取及儲存私人 Todo 資料。

路由群組也支援具有路由參數和條件約束的巢狀群組和複雜前置詞模式。 在下列範例中,對應至 user 群組的路由處理常式可以擷取外部群組前置詞中定義的 {org}{group} 路由參數。

前置詞也可能是空的。 這適用於將端點中繼資料或篩選條件新增至一組端點,而不需要變更路由模式。

var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");

將篩選條件或中繼資料新增至群組的行為,與將篩選條件或中繼資料個別新增至每個端點,再新增任何可能已新增至內部群組或特定端點的額外篩選條件或中繼資料的方式相同。

var outer = app.MapGroup("/outer");
var inner = outer.MapGroup("/inner");

inner.AddEndpointFilter((context, next) =>
{
    app.Logger.LogInformation("/inner group filter");
    return next(context);
});

outer.AddEndpointFilter((context, next) =>
{
    app.Logger.LogInformation("/outer group filter");
    return next(context);
});

inner.MapGet("/", () => "Hi!").AddEndpointFilter((context, next) =>
{
    app.Logger.LogInformation("MapGet filter");
    return next(context);
});

在上述範例中,即使已新增第二個要求,外部篩選條件也會在內部篩選條件之前記錄傳入要求。 由於篩選條件已套用至不同的群組,因此彼此相對的新增順序並不重要。 如果套用至相同的群組或特定端點,則篩選條件的新增順序就很重要。

/outer/inner/ 的要求會記錄下列內容:

/outer group filter
/inner group filter
MapGet filter

gRPC

JSON 轉碼

gRPC JSON 轉碼是 ASP.NET Core 的擴充功能,可針對 gRPC 服務建立 RESTful JSON API。 gRPC JSON 轉碼允許:

  • 應用程式使用熟悉 HTTP 概念呼叫 gRPC 服務。
  • ASP.NET Core gRPC 應用程式同時支援 gRPC 和 RESTful JSON API,無需複寫功能。
  • 實驗性支援透過與 Swashbuckle 整合,從轉碼的 RESTful API 產生 OpenAPI。

如需詳細資訊,請參閱 ASP.NET Core gRPC 應用程式中的 gRPC JSON 轉碼使用 OpenAPI 搭配 gRPC JSON 轉碼 ASP.NET Core 應用程式

ASP.NET Core 中的 gRPC 健康情況檢查

gRPC 健康情況檢查通訊協定是報告 gRPC 伺服器應用程式健康情況的標準。 應用程式會將健康情況檢查公開為 gRPC 服務。 它們通常與外部監視服務搭配使用,以檢查應用程式的狀態。

gRPC ASP.NET Core 已新增透過 Grpc.AspNetCore.HealthChecks 套件進行 gRPC 健康情況檢查的內建支援。 來自 .NET 健康情況檢查的結果會回報給呼叫端。

如需詳細資訊,請參閱 ASP.NET Core 中的健康情況檢查

改善呼叫認證支援

呼叫認證是設定 gRPC 用戶端以將驗證權杖傳送至伺服器的建議方式。 gRPC 用戶端支援兩項新功能,讓呼叫認證更容易使用:

  • 支援使用純文字連線的呼叫認證。 先前,如果連線受到 TLS 保護,gRPC 呼叫只會傳送呼叫認證。 GrpcChannelOptions 有一項稱為 UnsafeUseInsecureChannelCallCredentials 新設定,可自訂此行為。 不使用 TLS 來保護連線會有安全隱患。
  • gRPC 用戶端處理站 即提供稱為 AddCallCredentials 的新方法。 AddCallCredentials 是為 gRPC 用戶端設定呼叫認證的快速方式,而且能與相依性插入 (DI) 充分整合。

下列程式碼會設定 gRPC 用戶端處理站以傳送 Authorization 中繼資料:

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
       o.Address = new Uri("https://localhost:5001");
    })
    .AddCallCredentials((context, metadata) =>
    {
       if (!string.IsNullOrEmpty(_token))
       {
          metadata.Add("Authorization", $"Bearer {_token}");
       }
       return Task.CompletedTask;
    });

如需詳細資訊,請參閱 使用 gRPC 用戶端處理站設定持有人權杖

SignalR

用戶端結果

伺服器現在支援向用戶端要求結果。 這需要伺服器使用 ISingleClientProxy.InvokeAsync,而且需要用戶端透過其 .On 處理常式來傳回結果。 強型別中樞也可以從介面方法傳回值。

如需詳細資訊,請參閱用戶端結果

SignalR 中樞方法的相依性插入

SignalR 中樞方法現在支援透過相依性插入 (DI) 來插入服務。

中樞建構函式可以接受來自 DI 的服務作為參數,而這些參數可以儲存至類別上的屬性,以用於中樞方法。 如需詳細資訊,請參閱將服務插入中樞

Blazor

處理位置變更事件和瀏覽狀態

在 .NET 7 中,Blazor 支援位置變更事件和維護瀏覽狀態。 這可讓您警告使用者未儲存的工作,或在使用者進行頁面導覽時執行相關動作。

如需詳細資訊,請參閱路由和導覽文章的下列各節:

空白 Blazor 專案範本

Blazor 有兩個新的專案範本,可供您從頭開始作業。 新的 Blazor Server 應用程式空白Blazor WebAssembly 應用程式空白專案範本除了沒有範本程式碼外,大抵與非空白專案範本類似。 這些空白範本只包含基本首頁,我們已從中移除 Bootstrap,讓您可以從不同的 CSS 架構開始。

如需詳細資訊,請參閱下列文章:

Blazor 自訂元素

Microsoft.AspNetCore.Components.CustomElements 套件會使用 Blazor 來啟用建置標準型自訂 DOM 元素

如需詳細資訊,請參閱 ASP.NET Core Razor 元件

繫結修飾詞 (@bind:after@bind:get@bind:set)

重要

@bind:after/@bind:get/@bind:set 功能目前正接收進一步的更新。 若要利用最新的更新,請確認您已安裝最新的 SDK

不支援使用事件回呼參數 ([Parameter] public EventCallback<string> ValueChanged { get; set; })。 相反地,會將 Action-returning 或 Task-returning 方法傳遞至 @bind:set/@bind:after

如需詳細資訊,請參閱以下資源:

在 .NET 7 中,您可以使用新的 @bind:after 修飾詞在繫結事件完成後執行非同步邏輯。 在下列範例中,PerformSearch 非同步方法會在偵測到搜尋文字的任何變更之後自動執行:

<input @bind="searchText" @bind:after="PerformSearch" />

@code {
    private string searchText;

    private async Task PerformSearch()
    {
        ...
    }
}

在 .NET 7 中,為元件參數設定繫結也比較容易。 元件可以藉由定義一對參數來支援雙向資料繫結:

  • @bind:get:指定要繫結的值。
  • @bind:set:指定發生值變更時的回呼。

@bind:get@bind:set 修飾元一律會一起使用。

範例:

@* Elements *@

<input type="text" @bind="text" @bind:after="() => { }" />

<input type="text" @bind:get="text" @bind:set="(value) => { }" />

<input type="text" @bind="text" @bind:after="AfterAsync" />

<input type="text" @bind:get="text" @bind:set="SetAsync" />

<input type="text" @bind="text" @bind:after="() => { }" />

<input type="text" @bind:get="text" @bind:set="(value) => { }" />

<input type="text" @bind="text" @bind:after="AfterAsync" />

<input type="text" @bind:get="text" @bind:set="SetAsync" />

@* Components *@

<InputText @bind-Value="text" @bind-Value:after="() => { }" />

<InputText @bind-Value:get="text" @bind-Value:set="(value) => { }" />

<InputText @bind-Value="text" @bind-Value:after="AfterAsync" />

<InputText @bind-Value:get="text" @bind-Value:set="SetAsync" />

<InputText @bind-Value="text" @bind-Value:after="() => { }" />

<InputText @bind-Value:get="text" @bind-Value:set="(value) => { }" />

<InputText @bind-Value="text" @bind-Value:after="AfterAsync" />

<InputText @bind-Value:get="text" @bind-Value:set="SetAsync" />

@code {
    private string text = "";

    private void After(){}
    private void Set() {}
    private Task AfterAsync() { return Task.CompletedTask; }
    private Task SetAsync(string value) { return Task.CompletedTask; }
}

如需 InputText 元件的詳細資訊,請參閱 ASP.NET Core Blazor 輸入元件

熱重新載入改善

在 .NET 7 中,熱重新載入支援包含下列項目:

  • 當移除某個值時,元件會將其參數重設為預設值。
  • Blazor WebAssembly:
    • 新增類型。
    • 新增巢狀類別。
    • 將靜態和 執行個體方法新增至現有的類型。
    • 將靜態欄位和方法新增至現有的類型。
    • 將靜態 Lambda 新增至現有的方法。
    • 將擷取 this 的 Lambda 新增至先前已擷取 this 的現有方法。

Blazor WebAssembly 中使用 MSAL 的動態驗證要求

.NET 7 的新功能 Blazor WebAssembly 支援使用自訂參數在執行階段建立動態驗證要求,據以處理進階驗證案例。

如需詳細資訊,請參閱下列文章:

Blazor WebAssembly 偵錯改進

Blazor WebAssembly 偵錯具有下列改善:

  • 支援 Just My Code 設定,以顯示或隱藏不屬於使用者程式碼的類型成員。
  • 支援檢查多維陣列。
  • 呼叫堆疊現在會顯示非同步方法的正確名稱。
  • 改善的運算式評估。
  • 正確處理衍生成員上的 new 關鍵字。
  • 支援 System.Diagnostics 中的偵錯工具相關屬性。

WebAssembly 上的 System.Security.Cryptography 支援

.NET 6 在 WebAssembly 上執行時支援 SHA 系列雜湊演算法。 .NET 7 會盡可能利用 SubtleCrypto 來啟用更多密碼編譯演算法,並在無法使用 SubtleCrypto 時回復至 .NET 實作。 .NET 7 中的 WebAssembly 支援下列演算法:

  • SHA1
  • SHA256
  • SHA384
  • SHA512
  • HMACSHA1
  • HMACSHA256
  • HMACSHA384
  • HMACSHA512
  • AES-CBC
  • PBKDF2
  • HKDF

如需詳細資訊,請參閱 以 browser-wasm 為目標的開發人員可以使用 Web Crypto API (dotnet/runtime #40074)

將服務插入自訂驗證屬性

您現在可以將服務插入自訂驗證屬性。 Blazor 會設定 ValidationContext,以便將其當作服務提供者。

如需詳細資訊,請參閱 ASP.NET Core Blazor 表單驗證

EditContext/EditForm 外部的 Input* 元件

表單外部的 Razor 元件標記中現在支援內建輸入元件。

如需詳細資訊,請參閱 ASP.NET Core Blazor 輸入元件

專案範本變更

去年 .NET 6 發行時,_Host 頁面 (Pages/_Host.chstml) 的 HTML 標記被分割成 .NET 6 Blazor Server 專案範本中的 _Host 頁面和新的 _Layout 頁面 (Pages/_Layout.chstml)。

而在 .NET 7 中,HTML 標記已與專案範本中的 _Host 頁面重新結合。

Blazor 專案範本已進行了幾項其他變更。 在文件中列出對範本的所有變更並不可行。 若要將應用程式移轉到 .NET 7 以採用所有變更,請參閱從 ASP.NET Core 6.0 移轉至 7.0

實驗性 QuickGrid 元件

新的 QuickGrid 元件提供了一個方便的資料格元件,可滿足最常見的需求,並做為建構 Blazor 資料格元件時的參考架構和效能基準。

如需詳細資訊,請參閱 ASP.NET Core Blazor QuickGrid 元件

即時示範:Blazor 範例應用程式的 QuickGrid

虛擬化增強功能

.NET 7 中的虛擬化增強功能:

  • Virtualize 元件支援使用文件本身做為捲動根目錄,以替代套用了 overflow-y: scroll 的某些其他元素。
  • 如果 Virtualize 元件放置於需要特定子標籤名稱的元素內,SpacerElement 可讓您取得或設定虛擬化空白字元標籤名稱。

如需詳細資訊,請參閱虛擬化文章的下列各節:

MouseEventArgs 更新

已將 MovementXMovementY 新增至 MouseEventArgs

如需詳細資訊,請參閱 ASP.NET Core Blazor 事件處理

新增 Blazor 載入頁面

Blazor WebAssembly 專案範本擁有新的載入 UI,會顯示載入應用程式的進度。

如需詳細資訊,請參閱 ASP.NET Core Blazor 啟動

改善 Blazor WebAssembly 中的驗證診斷

為了協助診斷 Blazor WebAssembly 應用程式中的驗證問題,已有詳細記錄可供使用。

如需詳細資訊,請參閱 ASP.NET Core Blazor 記錄

WebAssembly 上的 JavaScript Interop

JavaScript [JSImport]/[JSExport] Interop API 是一種新的低階機制,可供您在 Blazor WebAssembly 和 JavaScript 型應用程式中使用 .NET。 有了這個新的 JavaScript Interop 功能,您可以透過 .NET WebAssembly 執行階段從 JavaScript 叫用 .NET 程式碼,並從 .NET 呼叫 JavaScript 功能,而無需依賴 Blazor UI 元件模型。

如需詳細資訊,請參閱:

驗證狀態提供者的條件式註冊

在 .NET 7 發行之前,AuthenticationStateProvider 是在服務容器中向 AddScoped 註冊。 這使得對應用程式進行偵錯變得困難,因為在提供自訂實作時會強制執行特定的服務註冊順序。 由於內部架構隨著時間的推移而變化,現在已不再需要向 AddScoped 註冊 AuthenticationStateProvider

在開發人員程式碼中,請對驗證狀態提供者服務註冊進行下列變更:

- builder.Services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
+ builder.Services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

在上述範例中,ExternalAuthStateProvider 是開發人員的服務實作。

對 .NET WebAssembly 建置工具的改善

.NET 7 的 wasm-tools 工作負載中的新功能,可協助改善效能並處理例外狀況:

如需詳細資訊,請參閱 ASP.NET Core Blazor WebAssembly 建置工具和預先 (AOT) 編譯

Blazor Hybrid

外部 URL

已新增一個選項,允許在瀏覽器中開啟外部網頁。

如需詳細資訊,請參閱 ASP.NET Core Blazor Hybrid 路由和瀏覽

安全性

Blazor Hybrid 安全性案例有可用的新指引。 如需詳細資訊,請參閱下列文章:

效能

輸出快取中介軟體

輸出快取是一個新的中介軟體,可儲存來自 Web 應用程式的回應,並從快取提供回應而不是每次進行計算。 輸出快取與回應快取有以下差異:

  • 快取行為可在伺服器上設定。
  • 可以透過程式設計方式使快取項目失效。
  • 資源鎖定可降低快取亂竄驚群效應的風險。
  • 快取重新驗證表示伺服器可以傳回 304 Not Modified HTTP 狀態碼,而不是快取的回應本文。
  • 快取儲存媒體可延伸。

如需詳細資訊,請參閱 快取概觀輸出快取中介軟體

HTTP/3 改善

此發行版本:

  • 讓 ASP.NET Core 完全支援 HTTP/3,不再屬於實驗性質。
  • 改善 Kestrel 對 HTTP/3 的支援。 主要的兩個改善領域分別是與 HTTP/1.1 和 HTTP/2 的功能同位加上性能。
  • 透過 HTTP/3 提供對 UseHttps(ListenOptions, X509Certificate2) 的全面支援。 Kestrel 提供設定連線憑證的進階選項,例如連結至伺服器名稱指示 (SNI)
  • HTTP.sysIIS 上新增對 HTTP/3 的支援。

下列範例示範如何使用 SNI 回呼來解析 TLS 選項:

using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Https;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(options =>
{
    options.ListenAnyIP(8080, listenOptions =>
    {
        listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
        listenOptions.UseHttps(new TlsHandshakeCallbackOptions
        {
            OnConnection = context =>
            {
                var options = new SslServerAuthenticationOptions
                {
                    ServerCertificate = 
                         MyResolveCertForHost(context.ClientHelloInfo.ServerName)
                };
                return new ValueTask<SslServerAuthenticationOptions>(options);
            },
        });
    });
});

在 .NET 7 中進行了大量工作來減少 HTTP/3 配置。 您可以在下列 GitHub PR 中查看其中一些改善:

HTTP/2 效能改善

.NET 7 對 Kestrel 處理 HTTP/2 要求的方式進行了大幅重新架構。 具有繁忙 HTTP/2 連線的 ASP.NET Core 應用程式,將能感受到 CPU 使用量降低和輸送量提高。

先前,HTTP/2 多工處理實作是依賴鎖定來控制哪個要求可以寫入基礎 TCP 連線。 此種寫入鎖定已由安全執行緒佇列所取代。 現在,要求會排入佇列並由專責取用者進行處理,不用再爭奪哪個執行緒可以使用寫入鎖定。 原本浪費的 CPU 資源因而可供應用程式的其餘部分使用。

其中一個可以體現這些改善的地方是在 gRPC (一種使用了 HTTP/2 的熱門 RPC 架構) 中。 Kestrel + gRPC 基準顯示了大幅改善:

gRPC 伺服器串流效能

HTTP/2 框架撰寫程式碼做了變更,可於多個資料流嘗試在單一 HTTP/2 連線上寫入資料時提升效能。 我們現在會將 TLS 工作分派至執行緒集區,更快速地釋放寫入鎖定供其他資料流取用以便寫入資料。 發生此種寫入鎖定爭用的情況時,縮短等候時間將可大幅改善效能。 單一連線 (使用 TLS) 上有 70 個資料流的 gRPC 基準測試顯示,透過此變更,每秒要求數 (RPS) 提高了約 15%。

Http/2 WebSockets 支援

.NET 7 針對 Kestrel、SignalR JavaScript 用戶端和帶有 Blazor WebAssembly 的 SignalR 引進了基於 HTTP/2 的 Websockets 支援。

透過 HTTP/2 使用 WebSockets 會善用新功能,例如:

  • 標頭壓縮。
  • 多工處理,可減少對伺服器提出多個要求時所需的時間和資源。

在所有啟用 HTTP/2 之平台的 Kestrel 中,都提供這些支援的功能。 在瀏覽器和 Kestrel 中,會自動進行版本交涉,因此不需要新的 API。

如需詳細資訊,請參閱 Http/2 WebSockets 支援

對高核心電腦的 Kestrel 效能改善

Kestrel 會將 ConcurrentQueue<T> 用於多種用途。 其中一種用途是在 Kestrel 的預設通訊端傳輸中排程 I/O 作業。 根據相關聯的通訊端分割 ConcurrentQueue 可減少爭用,並增加擁有多 CPU 核心之電腦的輸送量。

對 .NET 6 上的高核心電腦進行分析顯示,Kestrel 的其他 ConcurrentQueue 執行個體之一 (Kestrel 用於快取位元組緩衝區的 PinnedMemoryPool) 存在嚴重爭用。

在 .NET 7 中,Kestrel 的記憶體集區會以與 I/O 佇列相同的方式進行分割,從而導致高核心電腦上的爭用率大大降低並提高輸送量。 在 80 核心 ARM64 虛擬機器上,我們發現 TechEmpower 純文字基準測試中的每秒回應次數 (RPS) 提高了 500% 以上。 在 48 個核心 AMD 虛擬機器上,我們的 HTTPS JSON 基準測試中改善了幾近 100%。

用於測量啟動時間的 ServerReady 事件

使用 EventSource 的應用程式可以測量啟動時間,據以了解並最佳化啟動效能。 Microsoft.AspNetCore.Hosting 中的新 ServerReady 事件代表伺服器準備好回應要求的時點。

伺服器

用於測量啟動時間的新 ServerReady 事件

新增了 ServerReady 事件來測量 ASP.NET Core 應用程式的啟動時間

IIS

IIS 中的陰影複製

將應用程式組件的陰影複製到適用於 IIS 的 ASP.NET Core 模組 (ANCM) 可藉由部署應用程式離線檔案,提供比停止應用程式更好的使用者體驗。

如需詳細資訊,請參閱 IIS 中的陰影複製

其他

Kestrel 完整憑證鏈結改善

HttpsConnectionAdapterOptions 具有 X509Certificate2Collection 類型的新 ServerCertificateChain 屬性,可藉由允許指定包含中繼憑證的完整鏈結,讓您更輕鬆地驗證憑證鏈結。 如需詳細資訊,請參閱 dotnet/aspnetcore#21513

dotnet watch

改善 dotnet watch 的主控台輸出

dotnet watch 的主控台輸出已獲改善,可以更好地與 ASP.NET Core 的記錄保持一致,並透過😮表情符號😍脫穎而出。

以下是該項新輸出的呈現範例:

dotnet watch 的輸出

如需詳細資訊,請參閱這個 GitHub 提取要求

設定 dotnet watch 一律重新啟動以進行粗略編輯

粗略編輯是指無法熱重載的編輯。 若要將 dotnet watch 設定為一律重新啟動,而不提示粗略編輯,請將 DOTNET_WATCH_RESTART_ON_RUDE_EDIT 環境變數設定為 true

開發人員例外狀況頁面深色模式

歸功於 Patrick Westerhoff 的貢獻,開開發人員例外狀況頁面中加入了深色模式支援。 若要在瀏覽器中測試深色模式,請從開發人員工具頁面將模式設定為深色。 例如,在 Firefox 中:

F12 工具 FF 深色模式

在 Chrome 中:

F12 工具 Chrome 深色模式

使用 Program.Main 方法而非最上層陳述式的專案範本選項

.NET 7 範本包含一個選項,可以不使用最上層陳述式,並產生在 Program 類別上宣告的 namespaceMain 方法。

透過 .NET CLI,使用 --use-program-main 選項:

dotnet new web --use-program-main

使用 Visual Studio 時,在專案建立期間選取新的 [不要使用最上層陳述式] 核取方塊:

核取方塊

更新 Angular 和 React 範本

Angular 專案範本已更新至 Angular 14。 React 專案範本已更新至 React 18.2。

使用 dotnet user-jwts 管理開發中的 JSON Web 權杖

新的 dotnet user-jwts 命令列工具可以建立和管理應用程式專用的本機 JSON Web 權杖 (JWT)。 如需詳細資訊,請參閱使用 dotnet user-jwts 管理開發中的 JSON Web 權杖

支援 W3CLogger 中的其他要求標頭

您現在可以透過在 W3CLoggerOptions 上呼叫 AdditionalRequestHeaders() 來指定使用 W3C 記錄器時要記錄的其他請求標頭:

services.AddW3CLogging(logging =>
{
    logging.AdditionalRequestHeaders.Add("x-forwarded-for");
    logging.AdditionalRequestHeaders.Add("x-client-ssl-protocol");
});

如需詳細資訊,請參閱 W3CLogger 選項

要求解壓縮

新的要求解壓縮中介軟體

  • 可讓 API 端點接受具有壓縮內容的要求。
  • 使用 Content-Encoding HTTP 標頭來自動識別並解壓縮包含壓縮內容的要求。
  • 不需要撰寫程式碼來處理壓縮的要求。

如需詳細資訊,請參閱要求解壓縮中介軟體