從 ASP.NET Core 呼叫 Web API Blazor
注意
這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .NET 9 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支持原則。 如需目前的版本,請參閱 本文的 .NET 9 版本。
本文說明如何從 Blazor 應用程式呼叫 Web API。
包裹
System.Net.Http.Json
套件提供適用於 System.Net.Http.HttpClient 和 System.Net.Http.HttpContent 的擴充方法,這些方法會使用 System.Text.Json
來執行自動序列化及還原序列化。
System.Net.Http.Json
套件是由 .NET 共用架構所提供,且不需要將套件參考新增至應用程式。
範例應用程式
請參閱 dotnet/blazor-samples
GitHub 存放庫中的應用程式範例。
BlazorWebAppCallWebApi
從 Blazor Web App 呼叫外部(不在Blazor Web App)的待辦事項清單 Web API:
-
Backend
:Web API 應用程式,可根據 最小 API 維護待辦事項清單。 Web API 應用程式是與 Blazor Web App不同的應用程式,可能託管於不同的伺服器。 -
BlazorApp
/BlazorApp.Client
:這個 Blazor Web App 使用 HttpClient 來呼叫 Web API 應用程式,執行待辦事項清單的作業,比如建立、讀取、更新和刪除(CRUD)項目。
針對用戶端轉譯 (CSR),其中包含採用 CSR 的互動式 WebAssembly 組件和 Auto 組件,將會使用預先配置的 HttpClient,此配置已在用戶端專案 (Program
) 的 BlazorApp.Client
檔案中註冊。
builder.Services.AddScoped(sp =>
new HttpClient
{
BaseAddress = new Uri(builder.Configuration["FrontendUrl"] ?? "https://localhost:5002")
});
針對伺服器端渲染 (SSR),其中包括預先渲染和互動式的伺服器元件、WebAssembly 預先渲染元件,以及預先渲染或採用了 SSR 的 Auto 元件,在伺服器專案 (HttpClient) 的 Program
檔案中註冊的 BlazorApp
進行呼叫:
builder.Services.AddHttpClient();
呼叫內部 (位於 Blazor Web App) 的電影清單 API,其中該 API 位於 Blazor Web App 的伺服器專案中:
-
BlazorApp
:維護著電影清單的 Blazor Web App- 在伺服器的應用程式中對電影清單進行操作時,使用的是一般的 API 呼叫。
- WEB 型用戶端進行 API 呼叫時,Web API 會根據最小 API 用於電影清單作業。
-
BlazorApp.Client
:Blazor Web App的客戶端專案,其中包含用於管理電影清單使用者的互動式 WebAssembly 和 Auto 元件。
針對已採用 CSR 的互動式 WebAssembly 元件和 Auto 元件,API 呼叫是透過使用者端型服務 (ClientMovieService
) 進行,該服務使用的是註冊在用戶端專案的 HttpClient 檔案(Program
) 中已預先設定的 BlazorApp.Client
。 由於這些呼叫是透過公用或私人 Web 進行,因此電影清單 API 是 Web API。
下列範例會從 /movies
端點取得電影清單:
public class ClientMovieService(HttpClient http) : IMovieService
{
public async Task<Movie[]> GetMoviesAsync(bool watchedMovies) =>
await http.GetFromJsonAsync<Movie[]>("movies") ?? [];
}
針對 SSR,其中包含預先轉譯和互動式伺服器元件、預先轉譯的 WebAssembly 元件,以及預先轉譯或採用 SSR 的 Auto 元件,呼叫是直接透過伺服器型服務 (ServerMovieService
) 進行。 API 不仰賴網路,因此它是電影清單 CRUD 作業的標準 API。
下列範例會取得電影清單:
public class ServerMovieService(MovieContext db) : IMovieService
{
public async Task<Movie[]> GetMoviesAsync(bool watchedMovies) =>
watchedMovies ?
await db.Movies.Where(t => t.IsWatched).ToArrayAsync() :
await db.Movies.ToArrayAsync();
}
BlazorWebAppCallWebApi_Weather
天氣資料應用程式範例,使用串流轉譯處理天氣資料。
BlazorWebAssemblyCallWebApi
從 Blazor WebAssembly 應用程式呼叫待辦事項清單 Web API:
-
Backend
:Web API 應用程式,可根據 最小 API 維護待辦事項清單。 -
BlazorTodo
:這個 Blazor WebAssembly 應用程式使用預先設定的 HttpClient,為執行待辦事項清單 CRUD 作業呼叫 Web API。
呼叫外部 Web API 的用戶端案例
這些用戶端元件使用 HttpClient 實例來呼叫外部 Web API,這些實例通常是使用在 Program
檔案中註冊的預先配置 HttpClient 所建立的。
builder.Services.AddScoped(sp =>
new HttpClient
{
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
});
下列 Razor 元件會向 GitHub 分支的 Web API 提出要求,類似於在 ASP.NET Core 中使用 IHttpClientFactory 進行 HTTP 要求一文中的基本用法範例。
CallWebAPI.razor
:
@page "/call-web-api"
@using System.Text.Json
@using System.Text.Json.Serialization
@inject HttpClient Client
<h1>Call web API from a Blazor WebAssembly Razor component</h1>
@if (getBranchesError || branches is null)
{
<p>Unable to get branches from GitHub. Please try again later.</p>
}
else
{
<ul>
@foreach (var branch in branches)
{
<li>@branch.Name</li>
}
</ul>
}
@code {
private IEnumerable<GitHubBranch>? branches = [];
private bool getBranchesError;
private bool shouldRender;
protected override bool ShouldRender() => shouldRender;
protected override async Task OnInitializedAsync()
{
var request = new HttpRequestMessage(HttpMethod.Get,
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches");
request.Headers.Add("Accept", "application/vnd.github.v3+json");
request.Headers.Add("User-Agent", "HttpClientFactory-Sample");
var response = await Client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
branches = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(responseStream);
}
else
{
getBranchesError = true;
}
shouldRender = true;
}
public class GitHubBranch
{
[JsonPropertyName("name")]
public string? Name { get; set; }
}
}
在上述 C# 12 或更新版本的範例中,會針對 []
變數建立空陣列 (branches
)。 針對以 .NET 8 之前的 SDK 編譯的舊版 C#,請建立空陣列 (Array.Empty<GitHubBranch>()
)。
若要保護 .NET/C# 程式代碼和數據,請使用 ASP.NET Core 數據保護 功能搭配伺服器端 ASP.NET Core 後端 Web API。 用戶端 Blazor WebAssembly 應用程式會呼叫伺服器端 Web API,以保護應用程式功能和數據處理。
由於 Blazor WebAssembly, 應用程式通常無法跨來源對 Web API 進行直接呼叫。 典型的例外狀況如下所示:
從來源 'https://localhost:{PORT}'' 嘗試存取 '{URL}' 時被 CORS 原則封鎖:請求的資源上缺少 'Access-Control-Allow-Origin' 標頭。 如果無法透明化的回應符合您的需求,請將請求的模式設定為『no-cors』,以取得禁用 CORS 的資源。
即使您嘗試呼叫 SetBrowserRequestMode 並在 BrowserRequestMode 欄位中使用 NoCors
(1)來規避前述例外情況,由於 CORS 對 Web API 的來源限制,請求通常仍然會失敗,例如限制只允許來自特定來源的呼叫,或限制防止瀏覽器中的 JavaScript fetch
請求。 若要讓這類呼叫成功,唯一的方法是您所呼叫的 Web API 必須允許您的來源,使用正確配置的 CORS 組態來呼叫其來源。 大部分的外部 Web API 都不允許您設定其 CORS 原則。 若要處理這項限制,請採用下列任一策略:
維護您自己的伺服器端 ASP.NET Core 後端 Web API。 用戶端 Blazor WebAssembly 應用程式會呼叫伺服器端 Web API,而您的 Web API 會使用正確的 CORS 標頭,從其伺服器型 C# 程式代碼(而非瀏覽器)向外部 Web API 提出要求,並將結果傳回至用戶端 Blazor WebAssembly 應用程式。
使用代理服務將用戶端 Blazor WebAssembly 應用程式的請求代理轉發至外部的 Web API。 Proxy 服務會使用伺服器端應用程式代表用戶端提出要求,並在呼叫成功之後傳回結果。 在以下範例中,以 為基礎的 CloudFlare 的 CORS PROXY中,
{REQUEST URI}
占位元是請求 URI:@using System.Net @inject IHttpClientFactory ClientFactory ... @code { public async Task CallApi() { var client = ClientFactory.CreateClient(); var urlEncodedRequestUri = WebUtility.UrlEncode("{REQUEST URI}"); var request = new HttpRequestMessage(HttpMethod.Get, $"https://corsproxy.io/?{urlEncodedRequestUri}"); var response = await client.SendAsync(request); ... } }
呼叫外部 Web API 的伺服器端案例
以伺服器為基礎的元件會使用 HttpClient 執行個體 (通常是使用 IHttpClientFactory 所建立) 呼叫外部 Web API。 如需適用於伺服器端應用程式的指引,請參閱在 ASP.NET Core 中使用 IHttpClientFactory 提出 HTTP 要求。
伺服器端應用程式不包含 HttpClient 服務。 使用 HttpClient
工廠基礎設施向應用程式提供 HttpClient。
在 Program
檔案中:
builder.Services.AddHttpClient();
下列 Razor 元件會向 GitHub 分支的 Web API 提出要求,類似於在 ASP.NET Core 中使用 IHttpClientFactory 進行 HTTP 要求一文中的基本用法範例。
CallWebAPI.razor
:
@page "/call-web-api"
@using System.Text.Json
@using System.Text.Json.Serialization
@inject IHttpClientFactory ClientFactory
<h1>Call web API from a server-side Razor component</h1>
@if (getBranchesError || branches is null)
{
<p>Unable to get branches from GitHub. Please try again later.</p>
}
else
{
<ul>
@foreach (var branch in branches)
{
<li>@branch.Name</li>
}
</ul>
}
@code {
private IEnumerable<GitHubBranch>? branches = [];
private bool getBranchesError;
private bool shouldRender;
protected override bool ShouldRender() => shouldRender;
protected override async Task OnInitializedAsync()
{
var request = new HttpRequestMessage(HttpMethod.Get,
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches");
request.Headers.Add("Accept", "application/vnd.github.v3+json");
request.Headers.Add("User-Agent", "HttpClientFactory-Sample");
var client = ClientFactory.CreateClient();
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
branches = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(responseStream);
}
else
{
getBranchesError = true;
}
shouldRender = true;
}
public class GitHubBranch
{
[JsonPropertyName("name")]
public string? Name { get; set; }
}
}
在上述 C# 12 或更新版本的範例中,會針對 []
變數建立空陣列 (branches
)。 針對以 .NET 8 之前的 SDK 編譯的舊版 C#,請建立空陣列 (Array.Empty<GitHubBranch>()
)。
如需其他工作範例,請參閱 ASP.NET Core Blazor 檔案上傳一文中的伺服器端檔案上傳範例,將檔案上傳至 Web API 控制器。
Web API 呼叫的服務抽象層
本章節適用於 Blazor Web App,該程式會維護伺服器專案中的 Web API,或是將 Web API 呼叫轉換為外部 Web API。
使用互動式 WebAssembly 和自動轉譯模式時,預設會預先轉譯元件。 Auto 元件一開始也會以互動方式從伺服器轉譯,然後 Blazor 套件組合便下載至用戶端,並啟動用戶端執行階段。 這表示使用這些轉譯模式的元件,應該設計為可以從客戶端和伺服器順利執行。 如果在用戶端執行時,元件必須呼叫伺服器專案型 API,或將要求轉換為外部 Web API (位於 Blazor Web App外部),建議將服務介面後方的 API 呼叫抽象化,並實作服務的用戶端和伺服器版本:
- 用戶端版本會使用預先設定的 HttpClient 呼叫 Web API。
- 伺服器版本通常可以直接存取伺服器端資源。 不建議在伺服器上插入會呼叫回伺服器的 HttpClient,因為這樣的網路請求通常是不必要的。 或者,API 可能位於伺服器專案之外,但伺服器需要一個服務抽象層來以某種方式轉換請求,例如在代理請求中新增一個存取權杖。
使用 WebAssembly 轉譯模式時,您也可以選擇停用預先轉譯,讓元件只從用戶端轉譯。 如需詳細資訊,請參閱 ASP.NET Core Blazor 轉譯模式。
範例 (應用程式範例):
-
BlazorWebAppCallWebApi
應用程式範例中的電影清單 Web API。 - 在
BlazorWebAppCallWebApi_Weather
範例應用程式中,實現即時串流渲染的天氣數據網路 API。 - 回傳至用戶端的天氣資料,會在
BlazorWebAppOidc
(非 BFF 模式)或BlazorWebAppOidcBff
(BFF 模式)的應用程式範例中出現。 這些應用程式示範安全的 (web) API 呼叫。 如需詳細資訊,請參閱 使用 OpenID Connect (OIDC) 保護ASP.NET Core Blazor Web App。
Blazor Web App 外部 Web API
本章節適用於 Blazor Web App,該程式會呼叫個別 (外部) 專案維護的 Web API (可能託管於不同的伺服器)。
Blazor Web App通常會在用戶端預先轉譯 WebAssembly 元件,而 Auto 元件則會在伺服器上執行靜態或互動式伺服器端呈現 (SSR) 時進行轉譯。
HttpClient 服務預設不會在 Blazor Web App的主要專案註冊。 如果應用程式只使用 HttpClient 專案中註冊的 .Client
服務執行,如新增 HttpClient
服務一節所述,則執行應用程式會導致執行階段錯誤:
InvalidOperationException:無法為類型為 '...{COMPONENT}' 的屬性 'Http' 提供值。 沒有類型為 'System.Net.Http.HttpClient' 的已註冊服務。
使用下列其中一個方法:
將 HttpClient 服務新增至伺服器專案,以讓 HttpClient 在 SSR 過程中可用。 在伺服器計畫的
Program
檔案中,使用下列服務註冊:builder.Services.AddHttpClient();
HttpClient 服務由共用架構提供,因此不需要應用程式專案檔中的套件參考。
範例:
BlazorWebAppCallWebApi
應用程式範例中的待辦事項清單 Web API如果呼叫 Web API 的 WebAssembly 元件不需要預先轉譯,請遵循 ASP.NET Core Blazor 轉譯模式的指導停用預先轉譯。 如果您採用這個方法,則不需要將 HttpClient 服務新增至 Blazor Web App 的主要專案,因為不會在伺服器預先解譯元件。
如需詳細資訊,請參閱用戶端服務無法於預渲染過程中解決。
預渲染資料
預先轉譯時,元件會轉譯兩次:先以靜態方式轉譯,然後以互動方式轉譯。 狀態不會自動從預先轉譯的元件流向互動式元件。 如果元件執行非同步初始化作業,並在初始化期間轉譯不同狀態的不同內容,例如「載入中...」進度指示器,則元件轉譯兩次時,您可能會看到閃爍。
使用 Persistent Component State API (如 BlazorWebAppCallWebApi
和 BlazorWebAppCallWebApi_Weather
應用程式範例 所示) 流通預先轉譯的狀態,即可解決這個問題。 元件以互動方式呈現時,可以用相同的狀態以相同的方式呈現。 不過,API 目前無法與增強型瀏覽搭配使用,但是在頁面 (data-enhanced-nav=false
) 連結停用增強型瀏覽即可解決這個問題。 如需詳細資訊,請參閱以下資源:
用戶端要求串流
針對使用 HTTP/2 通訊協定和 HTTPS 的 Chromium 型瀏覽器(例如 Google Chrome 和 Microsoft Edge),用戶端 Blazor 會使用 Streams API,允許 要求串流。
若要啟用要求串流,請將 SetBrowserRequestStreamingEnabled 設定為 true
上的 HttpRequestMessage。
在下列檔案上傳範例中:
-
content
是檔案的 HttpContent。 -
/Filesave
是 Web API 端點。 -
Http
是 HttpClient。
var request = new HttpRequestMessage(HttpMethod.Post, "/Filesave");
request.SetBrowserRequestStreamingEnabled(true);
request.Content = content;
var response = await Http.SendAsync(request);
串流要求:
- 需要 HTTPS 通訊協定,且無法在 HTTP/1.x 上運作。
- 包含主體內容,但不包含
Content-Length
標頭。 要進行跨原始來源串流要求,必須有CORS預檢請求。
如需使用 InputFile 元件上傳檔案的詳細資訊,請參閱 ASP.NET Core Blazor 檔案上傳,以及 使用用戶端轉譯 (CSR)將檔案上傳至伺服器範例。
新增 HttpClient
服務
本節的指導適用於用戶端案例。
用戶端元件會使用預先設定的 HttpClient 服務來呼叫 Web API,其著重於將要求傳回來源伺服器。 其他 Web API 的其他 HttpClient 服務組態可以在開發人員程式碼中建立。 要求是使用 Blazor JSON 輔助工具或使用 HttpRequestMessage 撰寫。 要求可以包含Fetch API 選項設定。
唯有針對應用程式中單一 HttpClient 執行個體呼叫單一 Web API 時,本節的設定範例才有用。 應用程式必須呼叫多個 Web API 時,每個 API 都有自己的基底位址 (Base Address) 和設定,您可以採用本文稍後會說明的下列方法:
-
HttpClient
具名為IHttpClientFactory
:每個 Web API 都會獲得唯一的名稱。 應用程式程式碼或 Razor 元件呼叫 Web API 時,會使用具名 HttpClient 執行個體呼叫。 -
具類型的
HttpClient
:每個 Web API 都有類型。 應用程式程式碼或 Razor 元件呼叫 Web API 時,會使用特定類型的 HttpClient 執行個體進行呼叫。
在 Program
檔案中,如果用來建立應用程式的 HttpClient 專案範本尚未包含 Blazor 服務,請添加該服務。
builder.Services.AddScoped(sp =>
new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
上述範例使用 builder.HostEnvironment.BaseAddress
(IWebAssemblyHostEnvironment.BaseAddress) 設定應用程式的基址,並取得該基址。基址通常從主機頁面中的 <base>
href
標籤的值獲得。
使用用戶端本身基底位址 (Base Address) 最常見的使用案例如下:
-
.Client
(.NET 8 或更新版本) 的用戶端專案 (Blazor Web App) 會從 WebAssembly 元件,或是從在 WebAssembly 用戶端執行的程式碼,向伺服器應用程式中的 API 進行 Web API 呼叫。 - 裝載 Client 應用程式的用戶端專案 (Blazor WebAssembly) 會向伺服器專案 (Server) 進行 Web API 呼叫。 請注意,.NET 8 或更新版本不再提供裝載 Blazor WebAssembly 項目範本。 不過,.NET 8 仍支援裝載 Blazor WebAssembly 的應用程式。
如果您要呼叫外部 Web API (不在與用戶端應用程式相同的 URL 空間),請將 URI 設定為 Web API 的基底位址 (Base Address)。 下列範例會將 Web API 的基底位址 (Base Address) 設定為 https://localhost:5001
,其中會執行個別的 Web API 應用程式,並準備好回應來自用戶端應用程式的要求:
builder.Services.AddScoped(sp =>
new HttpClient { BaseAddress = new Uri("https://localhost:5001") });
JSON 助手
HttpClient 可做為預先設定的服務,以便將要求傳回原始伺服器。
HttpClient 和 JSON 協助程式(System.Net.Http.Json.HttpClientJsonExtensions)也可用來呼叫協力廠商 Web API 端點。 HttpClient 是使用瀏覽器的提取 API 來實作,並受限於其限制,包括強制執行相同原始來源原則,本文稍後會在跨原始來源資源共用 (CORS) 一節中討論。
用戶端的基底位址會設定為原始伺服器的位址。 使用@inject
指令將HttpClient執行個體插入至元件中:
@using System.Net.Http
@inject HttpClient Http
使用 System.Net.Http.Json 命名空間來存取 HttpClientJsonExtensions,包括 GetFromJsonAsync、PutAsJsonAsync 和 PostAsJsonAsync:
@using System.Net.Http.Json
下列各節說明 JSON 協助程式:
System.Net.Http 包含傳送 HTTP 要求及接收 HTTP 回應的其他方法,例如傳送 DELETE 要求。 如需詳細資訊,請參閱 DELETE 和其他擴充方法一節。
從 JSON 取得(GetFromJsonAsync
)
GetFromJsonAsync 會傳送 HTTP GET 要求,並剖析 JSON 回應本文以建立物件。
在下列元件程式碼中,元件會顯示 todoItems
。 當元件完成初始化時,會呼叫 GetFromJsonAsync (OnInitializedAsync
)。
todoItems = await Http.GetFromJsonAsync<TodoItem[]>("todoitems");
POST 請求格式為 JSON(PostAsJsonAsync
)
PostAsJsonAsync 會將 POST 請求傳送至指定的 URI,請求本文中包含以 JSON 序列化的值。
在下列元件程式碼中,newItemName
是由元件的繫結元素所提供。 選取 AddItem
元素會觸發 <button>
方法。
await Http.PostAsJsonAsync("todoitems", addItem);
PostAsJsonAsync 會傳回 HttpResponseMessage。 若要從回應訊息還原序列化 JSON 內容,請使用 ReadFromJsonAsync 擴充方法。 下列範例會以陣列的形式讀取 JSON 天氣資料:
var content = await response.Content.ReadFromJsonAsync<WeatherForecast[]>() ??
Array.Empty<WeatherForecast>();
以 JSON 表示(PutAsJsonAsync
)
PutAsJsonAsync 傳送具有 JSON 編碼內容的 HTTP PUT 要求。
在下列元件程式碼中,editItem
和 Name
的值 IsCompleted
是由元件的繫結元素所提供。 當項目在 UI 的另一個部分被選取 (未顯示) 且呼叫 EditItem
時,會設定項目的 Id
。 選取 SaveItem
元素會觸發 <button>
方法。 為了簡潔,下列範例不顯示載入 todoItems
。 如需載入項目的範例,請參閱 從 JSON 獲取(GetFromJsonAsync
) 一節。
await Http.PutAsJsonAsync($"todoitems/{editItem.Id}", editItem);
PutAsJsonAsync 會傳回 HttpResponseMessage。 若要從回應訊息還原序列化 JSON 內容,請使用 ReadFromJsonAsync 擴充方法。 下列範例會以陣列的形式讀取 JSON 天氣資料:
var content = await response.Content.ReadFromJsonAsync<WeatherForecast[]>() ??
Array.Empty<WeatherForecast>();
修補檔為 JSON(PatchAsJsonAsync
)
PatchAsJsonAsync 傳送具有 JSON 編碼內容的 HTTP 修補檔要求。
注意
如需詳細資訊,請參閱 ASP.NET Core Web API 中的 JsonPatch。
在下列範例中, PatchAsJsonAsync 會以具有轉義引號的純文字字串形式接收 JSON 修補檔文件:
await Http.PatchAsJsonAsync(
$"todoitems/{id}",
"[{\"operationType\":2,\"path\":\"/IsComplete\",\"op\":\"replace\",\"value\":true}]");
從 C# 11 (.NET 7) 起,你可以將 JSON 字串寫成 原始字串語法。 使用 StringSyntaxAttribute.Json 字段為程式代碼分析工具的 [StringSyntax]
屬性 指定 JSON 語法。
@using System.Diagnostics.CodeAnalysis
...
@code {
[StringSyntax(StringSyntaxAttribute.Json)]
private const string patchOperation =
"""[{"operationType":2,"path":"/IsComplete","op":"replace","value":true}]""";
...
await Http.PatchAsJsonAsync($"todoitems/{id}", patchOperation);
}
PatchAsJsonAsync 會傳回 HttpResponseMessage。 若要從回應訊息還原序列化 JSON 內容,請使用 ReadFromJsonAsync 擴充方法。 下列範例將 JSON 待辦事項資料讀取為陣列。 如果這個方法沒有傳回事項資料,則會建立空陣列,因此 content
在陳述式執行之後不會是 null:
var response = await Http.PatchAsJsonAsync(...);
var content = await response.Content.ReadFromJsonAsync<TodoItem[]>() ??
Array.Empty<TodoItem>();
未編碼的 PATCH 檔以縮排、間距及未轉義的引號編排,顯示為以下 JSON:
[
{
"operationType": 2,
"path": "/IsComplete",
"op": "replace",
"value": true
}
]
為了簡化在發出修補檔要求的應用程式中建立修補檔文件的程序,應用程式可以使用 .NET JSON 修補檔支援,如下列指導所示。
安裝 Microsoft.AspNetCore.JsonPatch
NuGet 套件,並使用套件的 API 功能來撰寫 PATCH 要求的 JsonPatchDocument。
將 @using
、System.Text.Json 和 System.Text.Json.Serialization 命名空間的 Microsoft.AspNetCore.JsonPatch 指示詞,新增至 Razor 元件頂端:
@using System.Text.Json
@using System.Text.Json.Serialization
@using Microsoft.AspNetCore.JsonPatch
撰寫 JsonPatchDocument 用於 TodoItem
,其中 IsComplete
設為 true
,並使用 Replace 方法。
var patchDocument = new JsonPatchDocument<TodoItem>()
.Replace(p => p.IsComplete, true);
將文件的操作 (patchDocument.Operations
) 傳遞給 PatchAsJsonAsync 呼叫:
private async Task UpdateItem(long id)
{
await Http.PatchAsJsonAsync(
$"todoitems/{id}",
patchDocument.Operations,
new JsonSerializerOptions()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
});
}
只有在屬性等於其類型的預設值時,JsonSerializerOptions.DefaultIgnoreCondition 才會設定為 JsonIgnoreCondition.WhenWritingDefault 以忽略屬性。
如果您想以美觀的格式顯示 JSON 內容,請將 JsonSerializerOptions.WriteIndented 設定為 true
。 撰寫縮排 JSON 並不會影響處理修補檔要求,且通常不會在 Web API 要求的生產應用程式中執行。
請遵循 ASP.NET Core Web API 中的 JsonPatch 一文中的指導,將 PATCH 控制器動作新增至 Web API。 或者,您可以使用下列步驟,以最小 API 的形式實作 PATCH 要求處理。
將 Microsoft.AspNetCore.Mvc.NewtonsoftJson
NuGet 套件的套件參考新增至 Web API 應用程式。
注意
不需要將 Microsoft.AspNetCore.JsonPatch
套件的套件參考新增至應用程式,因為對 Microsoft.AspNetCore.Mvc.NewtonsoftJson
套件的參考會自動加入 Microsoft.AspNetCore.JsonPatch
的套件參考。
在 Program
檔案中為 @using
命名空間新增 Microsoft.AspNetCore.JsonPatch 指令:
using Microsoft.AspNetCore.JsonPatch;
提供 Web API 要求處理管道的端點:
app.MapPatch("/todoitems/{id}", async (long id, TodoContext db) =>
{
if (await db.TodoItems.FindAsync(id) is TodoItem todo)
{
var patchDocument =
new JsonPatchDocument<TodoItem>().Replace(p => p.IsComplete, true);
patchDocument.ApplyTo(todo);
await db.SaveChangesAsync();
return TypedResults.Ok(todo);
}
return TypedResults.NoContent();
});
警告
如同 ASP.NET Core Web API 中的 JsonPatch 文章中的其他範例,上述 PATCH API 不會保護 Web API 免於過度發佈攻擊。 如需詳細資訊,請參閱 教學課程:使用 ASP.NET Core建立控制器型 Web API。
如需完整運作的 PATCH 體驗,請參閱BlazorWebAppCallWebApi
應用程式範例。
DELETE (DeleteAsync
) 和其他擴充方法
System.Net.Http 包含傳送 HTTP 要求及接收 HTTP 回應的其他擴充方法。 HttpClient.DeleteAsync 是用來將 HTTP DELETE 要求傳送至 Web API。
在下列元件程式碼中,<button>
元素會呼叫 DeleteItem
方法。 綁定的 <input>
元素提供了要刪除項目的 id
。
await Http.DeleteAsync($"todoitems/{id}");
以 HttpClient
命名 IHttpClientFactory
支援 IHttpClientFactory 服務和具名 HttpClient 的組態。
注意
使用 HttpClient 中具名 IHttpClientFactory 的替代方法是使用型別化的 HttpClient。 如需詳細資訊,請參閱類型 HttpClient
一節。
將 Microsoft.Extensions.Http
NuGet 套件新增至應用程式。
在用戶端專案的 Program
檔案:
builder.Services.AddHttpClient("WebAPI", client =>
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
如果命名的用戶端要由預先轉譯的 Blazor Web App 用戶端元件使用,那麼服務註冊應該同時出現在伺服器專案和 .Client
專案中。 在伺服器上,builder.HostEnvironment.BaseAddress
會由 Web API 的基底位址 (Base Address) 取代,如下所述。
上述用戶端範例會使用 builder.HostEnvironment.BaseAddress
和 IWebAssemblyHostEnvironment.BaseAddress 設定基底位址,這樣即可取得用戶端應用程式的基底位址,而且通常是從主頁面 <base>
標籤的 href
值衍生出來的。
使用用戶端本身基底位址 (Base Address) 最常見的使用案例如下:
-
.Client
的用戶端專案 (Blazor Web App) 會從 WebAssembly/Auto 元件,或是從在 WebAssembly 用戶端執行的程式碼,向相同主機位址之伺服器應用程式中的 API 進行 Web API 呼叫。 - 裝載 Client 應用程式的用戶端專案 (Blazor WebAssembly) 會向伺服器專案 (Server) 進行 Web API 呼叫。
使用用戶端本身基底位址 (Base Address) 最常見的使用案例,是在裝載 Client 應用程式的用戶端專案 (Blazor WebAssembly) 中,對伺服器專案 (Server) 進行 Web API 呼叫。
如果您要呼叫外部 Web API (不在與用戶端應用程式相同的 URL 空間),或是在伺服器端應用程式設定服務 (例如處理伺服器用戶端元件的預先解譯),請將 URI 設定為 Web API 的基底位址 (Base Address)。 下列範例會將 Web API 的基底位址 (Base Address) 設定為 https://localhost:5001
,其中會執行個別的 Web API 應用程式,並準備好回應來自用戶端應用程式的要求:
builder.Services.AddHttpClient("WebAPI", client =>
client.BaseAddress = new Uri("https://localhost:5001"));
在下列元件程式碼中:
- 一個 IHttpClientFactory 的執行個體會建立一個具名的 HttpClient。
- 被命名為 HttpClient 的工具用於向
/forecast
的 Web API 發出獲取 JSON 格式天氣預報數據的 GET 請求。
@inject IHttpClientFactory ClientFactory
...
@code {
private Forecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
var client = ClientFactory.CreateClient("WebAPI");
forecasts = await client.GetFromJsonAsync<Forecast[]>("forecast") ?? [];
}
}
應用程式範例示範如何在其 BlazorWebAppCallWebApi
元件中使用具名 來呼叫 Web API。 如需在用戶端程式中使用具名 HttpClient 來呼叫 Microsoft Graph 的額外實作範例,請參閱 將 Graph API 與 ASP.NET Core 搭配使用Blazor WebAssembly。
若要在用戶端應用程式中查看呼叫命名 HttpClient 的 Microsoft Graph 的運作示範,請參閱 將 Graph API 與 ASP.NET Core 搭配使用Blazor WebAssembly。
已輸入HttpClient
具型別的 HttpClient 會使用應用程式的一個或多個 HttpClient 實例(預設或具名),以從一個或多個 Web API 端點取得資料。
注意
使用具型別 HttpClient 的替代方法是使用 HttpClient 中具名的 IHttpClientFactory。 如需詳細資訊,請參閱標示為 HttpClient
與 IHttpClientFactory
一節。
將 Microsoft.Extensions.Http
NuGet 套件新增至應用程式。
以下範例發出來自 Web API 於 /forecast
之 JSON 天氣預報資料的 GET 要求。
ForecastHttpClient.cs
:
using System.Net.Http.Json;
namespace BlazorSample.Client;
public class ForecastHttpClient(HttpClient http)
{
public async Task<Forecast[]> GetForecastAsync() =>
await http.GetFromJsonAsync<Forecast[]>("forecast") ?? [];
}
在用戶端專案的 Program
檔案:
builder.Services.AddHttpClient<ForecastHttpClient>(client =>
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
如果具體類型的用戶端是由 Blazor Web App 預先轉譯的用戶端元件所使用,那麼上述服務註冊應同時出現在伺服器專案和 .Client
專案中。 在伺服器上,builder.HostEnvironment.BaseAddress
會由 Web API 的基底位址 (Base Address) 取代,如下所述。
上述範例會使用 builder.HostEnvironment.BaseAddress
(IWebAssemblyHostEnvironment.BaseAddress) 來設定基底位址,這用來取得用戶端應用程式的基底位址,通常會源自於主機頁面 <base>
標籤的 href
值。
使用用戶端本身基底位址 (Base Address) 最常見的使用案例如下:
-
.Client
的用戶端專案 (Blazor Web App) 會從 WebAssembly/Auto 元件,或是從在 WebAssembly 用戶端執行的程式碼,向相同主機位址之伺服器應用程式中的 API 進行 Web API 呼叫。 - 裝載 Client 應用程式的用戶端專案 (Blazor WebAssembly) 會向伺服器專案 (Server) 進行 Web API 呼叫。
使用用戶端本身基底位址 (Base Address) 最常見的使用案例,是在裝載 Client 應用程式的用戶端專案 (Blazor WebAssembly) 中,對伺服器專案 (Server) 進行 Web API 呼叫。
如果您要呼叫外部 Web API (不在與用戶端應用程式相同的 URL 空間),或是在伺服器端應用程式設定服務 (例如處理伺服器用戶端元件的預先解譯),請將 URI 設定為 Web API 的基底位址 (Base Address)。 下列範例會將 Web API 的基底位址 (Base Address) 設定為 https://localhost:5001
,其中會執行個別的 Web API 應用程式,並準備好回應來自用戶端應用程式的要求:
builder.Services.AddHttpClient<ForecastHttpClient>(client =>
client.BaseAddress = new Uri("https://localhost:5001"));
元件會將具型別的HttpClient 插入,以呼叫 Web API。
在下列元件程式碼中:
- 會注入前述
ForecastHttpClient
的一個實例,從而建立具型別的 HttpClient。 - 這個輸入的 HttpClient 用於從 Web API 發出 GET 請求以獲取 JSON 天氣預報資料。
@inject ForecastHttpClient Http
...
@code {
private Forecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await Http.GetForecastAsync();
}
}
BlazorWebAppCallWebApi
應用程式範例 示範在其具類型的 HttpClient 元件中呼叫 Web API。 請注意,元件採用用戶端渲染 (CSR) (InteractiveWebAssembly
渲染模式) 並結合預渲染,因此具類型的用戶端服務註冊會同時出現在伺服器專案和 Program
專案的 .Client
檔案中。
Cookie為基礎的請求憑證
本節的指導適用於採用驗證的用戶端案例cookie。
針對被視為比持有人權杖驗證更安全的 cookie 型驗證,可以針對每個 Web API 請求,利用預先設定的 HttpClient 上的 AddHttpMessageHandler 呼叫 DelegatingHandler 來傳送 cookie 憑證。 處理常式會使用 SetBrowserRequestCredentials 來配置 BrowserRequestCredentials.Include,指示瀏覽器隨每個請求發送憑證,例如 cookie 或 HTTP 驗證標頭,這包括針對跨原始來源的請求。
CookieHandler.cs
:
public class CookieHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
request.Headers.Add("X-Requested-With", [ "XMLHttpRequest" ]);
return base.SendAsync(request, cancellationToken);
}
}
CookieHandler
會在 Program
檔案中註冊:
builder.Services.AddTransient<CookieHandler>();
訊息處理常式會新增至需要 HttpClient 驗證的任何預先設定 cookie:
builder.Services.AddHttpClient(...)
.AddHttpMessageHandler<CookieHandler>();
撰寫 HttpRequestMessage 時,請直接設定瀏覽器要求認證和標頭:
var requestMessage = new HttpRequestMessage() { ... };
requestMessage.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
requestMessage.Headers.Add("X-Requested-With", [ "XMLHttpRequest" ]);
HttpClient
和 HttpRequestMessage
具有 Fetch API 請求選項
本節的指導適用於採用持有人權杖驗證的用戶端案例。
HttpClient
(API 文件) 和 HttpRequestMessage 可用來自訂要求。 例如,您可以指定 HTTP 方法和要求標頭。 下列元件會向 Web API 端點提出 POST
要求,並顯示回應本文。
TodoRequest.razor
:
@page "/todo-request"
@using System.Net.Http.Headers
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject HttpClient Http
@inject IAccessTokenProvider TokenProvider
<h1>ToDo Request</h1>
<h1>ToDo Request Example</h1>
<button @onclick="PostRequest">Submit POST request</button>
<p>Response body returned by the server:</p>
<p>@responseBody</p>
@code {
private string? responseBody;
private async Task PostRequest()
{
var requestMessage = new HttpRequestMessage()
{
Method = new HttpMethod("POST"),
RequestUri = new Uri("https://localhost:10000/todoitems"),
Content =
JsonContent.Create(new TodoItem
{
Name = "My New Todo Item",
IsComplete = false
})
};
var tokenResult = await TokenProvider.RequestAccessToken();
if (tokenResult.TryGetToken(out var token))
{
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", token.Value);
requestMessage.Content.Headers.TryAddWithoutValidation(
"x-custom-header", "value");
var response = await Http.SendAsync(requestMessage);
var responseStatusCode = response.StatusCode;
responseBody = await response.Content.ReadAsStringAsync();
}
}
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
}
Blazor的 HttpClient 用戶端實作使用 Fetch API,並透過 擴充方法和 HttpRequestMessage 設定基礎特定請求的 WebAssemblyHttpRequestMessageExtensions。 使用泛型 SetBrowserRequestOption 擴充方法來設定其他選項。 Blazor 和基礎擷取 API 不會直接新增或修改要求標頭。 如需有關使用者代理程式(例如瀏覽器)如何與標頭互動的詳細資訊,請參閱外部使用者代理程式文件集和其他 Web 資源。
HTTP 回應通常會經過緩衝處理,以支援回應內容上的同步讀取。 若要啟用回應串流支援,請對要求使用 SetBrowserResponseStreamingEnabled 擴充方法。
若要在跨原始來源要求中包含認證,請使用 SetBrowserRequestCredentials 擴充方法:
requestMessage.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
如需 Fetch API 選項的詳細資訊,請參閱 MDN Web 文件:WindowOrWorkerGlobalScope.fetch():參數。
處理錯誤
當在開發人員程式碼中發生 Web API 回應錯誤時,進行錯誤處理。 例如,GetFromJsonAsync 預期從 Web API 取得格式為 application/json
的 Content-Type
的 JSON 回應。 如果回應不是 JSON 格式,則內容驗證會擲回 NotSupportedException。
在下列範例中,天氣預報資料要求的 URI 端點拼錯。 URI 應為 WeatherForecast
,但在呼叫中顯示為 WeatherForcast
,其中遺漏 e
中的字母 Forecast
。
GetFromJsonAsync 呼叫預期會傳回 JSON,但對於未處理的例外狀況,Web API 傳回的是帶有 text/html
的 Content-Type
的 HTML。 之所以發生未處理的例外狀況是因為找不到 /WeatherForcast
的路徑,中介軟體無法提供要求的頁面或檢視。
在用戶端上的 OnInitializedAsync ,當回應內容驗證為非屬 JSON 時,會擲回 NotSupportedException 。 例外狀況會攔截在 catch
區塊中,自訂邏輯可以記錄錯誤或向使用者顯示易記的錯誤訊息。
ReturnHTMLOnException.razor
:
@page "/return-html-on-exception"
@using {PROJECT NAME}.Shared
@inject HttpClient Http
<h1>Fetch data but receive HTML on unhandled exception</h1>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<h2>Temperatures by Date</h2>
<ul>
@foreach (var forecast in forecasts)
{
<li>
@forecast.Date.ToShortDateString():
@forecast.TemperatureC ℃
@forecast.TemperatureF ℉
</li>
}
</ul>
}
<p>
@exceptionMessage
</p>
@code {
private WeatherForecast[]? forecasts;
private string? exceptionMessage;
protected override async Task OnInitializedAsync()
{
try
{
// The URI endpoint "WeatherForecast" is misspelled on purpose on the
// next line. See the preceding text for more information.
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForcast");
}
catch (NotSupportedException exception)
{
exceptionMessage = exception.Message;
}
}
}
注意
上述範例僅供示範之用。 即使端點不存在或伺服器上發生未處理的例外狀況,Web API 仍可設定為傳回 JSON。
如需詳細資訊,請參閱處理 ASP.NET Core Blazor 應用程式中的錯誤。
跨來源資源共用 (CORS)
瀏覽器安全性通常會限制網頁向提供網頁的不同來源提出要求。 這項限制稱為相同來源原則。 相同來源原則會限制 (但不會防止) 惡意網站從另一個網站讀取敏感性資料。 若要向具有不同來源的端點提出要求,端點必須啟用跨原始來源資源共用 (CORS)。
如需伺服器端 CORS 詳細資訊,請參閱在 ASP.NET Core 中啟用跨原始來源要求 (CORS)。 本文的範例不會直接與 Razor 元件案例相關,但本文適用於學習一般 CORS 概念。
如需用戶端 CORS 要求的資訊,請參閱 ASP.NET Core Blazor WebAssembly 其他安全性案例。
防偽支援
若要將防偽支援新增至 HTTP 要求,請插入 AntiforgeryStateProvider
,並將 RequestToken
新增至標頭集合做為 RequestVerificationToken
:
@inject AntiforgeryStateProvider Antiforgery
private async Task OnSubmit()
{
var antiforgery = Antiforgery.GetAntiforgeryToken();
var request = new HttpRequestMessage(HttpMethod.Post, "action");
request.Headers.Add("RequestVerificationToken", antiforgery.RequestToken);
var response = await client.SendAsync(request);
...
}
如需詳細資訊,請參閱 ASP.NET Core Blazor 驗證和授權。
測試 Web API 存取的 Blazor 架構元件範例
有各種公用網路工具可用於直接測試 Web API 後端應用程式,例如 Firefox Browser Developer。 Blazor 架構的參考來源包含可用於測試的 HttpClient 測試資產:
HttpClientTest
GitHub 存放庫中的 dotnet/aspnetcore
資產
注意
.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要為特定版本選取標籤,請使用切換分支或標籤下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤。
其他資源
一般
- W3C 的跨原始來源資源共用 (CORS) \(英文\)
- 在 ASP.NET Core 中啟用跨原始來源要求(CORS):雖然內容適用於 ASP.NET Core 應用程式,而非 Razor 元件,但本文涵蓋一般 CORS 概念。
降低過度發佈攻擊的風險
Web API 可能容易受到「過度發佈」攻擊,其也稱為「大量指派」攻擊。 當惡意使用者向伺服器發出 HTML 表單 POST 要求,而該伺服器處理的資料屬於未在呈現的表單中顯示的屬性,且開發人員不希望使用者修改這些屬性時,就會發生過度提交攻擊。 「過度發佈」一詞的字面意思是惡意使用者已過度發佈表單。
如需緩解過度發佈攻擊的指引,請參閱 教學課程:使用 ASP.NET Core建立控制器型 Web API。
伺服器端
- Blazor Web App:包括如何使用來進行安全的 Web API 請求。
- 在 ASP.NET Core 中使用 IHttpClientFactory 發出 HTTP 要求
- 在 ASP.NET Core 中強制執行 HTTPS
- Kestrel HTTPS 端點組態
用戶端
- ASP.NET Core Blazor WebAssembly 其他安全性案例:包含使用 HttpClient 進行安全 Web API 要求的涵蓋範圍。
- 使用 Graph API 搭配 ASP.NET Core Blazor WebAssembly
- Fetch API