Blazor WebAssembly 應(yīng)用程序中進(jìn)行 HTTP 請(qǐng)求
翻譯自 Waqas Anwar 2021年5月13日的文章 《Making HTTP Requests in Blazor WebAssembly Apps》 [1]
在我的前篇文章《Blazor Server 應(yīng)用程序中進(jìn)行 HTTP 請(qǐng)求》中,我介紹了在 Blazor Server 應(yīng)用程序中進(jìn)行 HTTP 請(qǐng)求的相關(guān)技術(shù),在 Blazor Server App 中您可以訪問(wèn)所有的 .NET 類庫(kù)和組件。但如果您創(chuàng)建的是 Blazor WebAssembly 應(yīng)用程序,那么您的代碼將在客戶端的瀏覽器沙箱中運(yùn)行,您的選擇在某種程度上會(huì)受到限制。在本教程中,我將向您展示如何在 Blazor WebAssembly 應(yīng)用程序進(jìn)行 HTTP 請(qǐng)求。
Blazor WebAssembly 應(yīng)用程序中的 HttpClient 概述
Blazor WebAssembly 應(yīng)用程序使用預(yù)置的 HttpClient 服務(wù)調(diào)用 Web API。這個(gè)預(yù)置的 HttpClient 是使用瀏覽器的 Fetch API[2] 實(shí)現(xiàn)的,會(huì)有一些限制。HttpClient 還可以使用 Blazor JSON 幫助程序或 HttpRequestMessage 對(duì)象進(jìn)行 API 調(diào)用。默認(rèn)情況下,您只能向同源服務(wù)器發(fā)送 API 調(diào)用請(qǐng)求,不過(guò)如果第三方 API 支持跨域資源共享(CORS)的話,您也可以調(diào)用其他服務(wù)器上的 API。
命名空間 System.Net.Http.Json 為使用 System.Text.Json 執(zhí)行自動(dòng)序列化和反序列化的 HttpClient 提供了擴(kuò)展方法。這些擴(kuò)展方法將請(qǐng)求發(fā)送到一個(gè) Web API URI 并處理相應(yīng)的響應(yīng)。常用的方法有:
- GetFromJsonAsync:發(fā)送 HTTP GET 請(qǐng)求,并將 JSON 響應(yīng)正文解析成一個(gè)對(duì)象。
- PostAsJsonAsync:將 POST 請(qǐng)求發(fā)送到指定的 URI,并在請(qǐng)求正文中載有序列化為 JSON 的 value。
- PutAsJsonAsync:發(fā)送 HTTP PUT 請(qǐng)求,其中包含 JSON 編碼的內(nèi)容。
要理解如何將這些方法與 HttpClient 一起使用,我們需要?jiǎng)?chuàng)建兩個(gè)項(xiàng)目。第一個(gè)項(xiàng)目是一個(gè) Web API 項(xiàng)目,它向客戶端公開一個(gè) Web API。第二個(gè)項(xiàng)目是 Blazor WebAssembly 應(yīng)用程序,它向第一個(gè)項(xiàng)目中創(chuàng)建的 Web API 發(fā)送 HTTP 請(qǐng)求。
實(shí)現(xiàn)一個(gè) ASP.NET Core Web API
在本節(jié)中,我們將實(shí)現(xiàn)一個(gè)支持跨域資源共享 (CORS) 的 Web API,以便 Blazor WebAssembly 應(yīng)用程序可以調(diào)用此 API。在 Visual Studio 2019 中創(chuàng)建一個(gè)新的 Web API 項(xiàng)目 BlazorClientWebAPI。我們將創(chuàng)建一個(gè)簡(jiǎn)單的 API 來(lái)返回產(chǎn)品列表,所以首先要在項(xiàng)目中創(chuàng)建一個(gè) Models 文件夾,并在其中添加如下的 Product 類。
Product.cs
- public class Product
- {
- public int Id { get; set; }
- public string Name { get; set; }
- public decimal Price { get; set; }
- }
接下來(lái),創(chuàng)建一個(gè) Controllers 文件夾并在其中添加下面的 ProductsController。該控制器簡(jiǎn)單地從 GetProducts 方法返回一些模擬的產(chǎn)品數(shù)據(jù)。
ProductsController.cs
- [Route("api/[controller]")]
- [ApiController]
- public class ProductsController : ControllerBase
- {
- [HttpGet]
- public IActionResult GetProducts()
- {
- var products = new List<Product>()
- {
- new Product()
- {
- Id = 1,
- Name = "Wireless Mouse",
- Price = 29.99m
- },
- new Product()
- {
- Id = 2,
- Name = "HP Headphone",
- Price = 79.99m
- },
- new Product()
- {
- Id = 3,
- Name = "Sony Keyboard",
- Price = 119.99m
- }
- };
- return Ok(products);
- }
- }
現(xiàn)在如果您運(yùn)行該項(xiàng)目,并嘗試在瀏覽器中使用 URI api/products 訪問(wèn)該 API,您應(yīng)該能看到以 JSON 格式返回的產(chǎn)品數(shù)據(jù)。
在 ASP.NET Core Web API 中啟用 CORS
默認(rèn)情況下,瀏覽器安全性不允許一個(gè)網(wǎng)頁(yè)向除提供該網(wǎng)頁(yè)的域之外的其他域發(fā)送請(qǐng)求。這種約束稱之為同源策略。如果我們希望 Blazor WebAssembly 應(yīng)用程序或其他客戶端應(yīng)用程序使用上述 Web API,那么我們必須啟用跨域資源共享 (CORS)。打開 Startup.cs 文件,并在 ConfigureServices 方法中調(diào)用 AddCors 方法。
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddCors(policy =>
- {
- policy.AddPolicy("CorsPolicy", opt => opt
- .AllowAnyOrigin()
- .AllowAnyHeader()
- .AllowAnyMethod());
- });
- services.AddControllers();
- }
同時(shí),在 Startup.cs 文件的 Configure 方法中添加以下代碼行。
- app.UseCors("CorsPolicy");
有關(guān)使用 ASP.NET Core 應(yīng)用程序的 CORS 的詳細(xì)信息,請(qǐng)參閱 《Enable Cross-Origin Requests (CORS) in ASP.NET Core》[3]。
實(shí)現(xiàn) Blazor WebAssembly 應(yīng)用程序
在創(chuàng)建上述 Web API 項(xiàng)目的同一解決方案中添加一個(gè)新的 Blazor WebAssembly 應(yīng)用程序項(xiàng)目 BlazorClientWebAPIsDemo。
我們需要確保的第一件事是,在項(xiàng)目文件中有 System.Net.Http.Json 的引用。如果沒(méi)有,那么您可以添加該引用。
- <Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
- <PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
- </PropertyGroup>
- <ItemGroup>
- <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.1" />
- <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.1" PrivateAssets="all" />
- <PackageReference Include="System.Net.Http.Json" Version="5.0.0" />
- </ItemGroup>
- </Project>
接下來(lái),我們需要在 Program.cs 文件中配置 HttpClient 服務(wù)。確保提供了要從 Blazor WebAssembly 應(yīng)用程序調(diào)用的 Web API 的基地址。
Program.cs
- public static async Task Main(string[] args)
- {
- var builder = WebAssemblyHostBuilder.CreateDefault(args);
- builder.RootComponents.Add<App>("#app");
- builder.Services.AddScoped(sp => new HttpClient
- {
- BaseAddress = new Uri("http://localhost:5000/api/")
- });
- await builder.Build().RunAsync();
- }
為了使用產(chǎn)品 API,我們?cè)?Pages 文件夾中創(chuàng)建一個(gè) Products.razor 組件。該視圖非常簡(jiǎn)單,因?yàn)樗皇堑a(chǎn)品列表并使用簡(jiǎn)單的 HTML 表格來(lái)顯示它們。
Products.razor
- @page "/products"
- <h1>Products</h1>
- @if (products == null)
- {
- <p><em>Loading...</em></p>
- }
- else
- {
- <table class="table">
- <thead>
- <tr>
- <th>Id</th>
- <th>Name</th>
- <th>Price</th>
- </tr>
- </thead>
- <tbody>
- @foreach (var forecast in products)
- {
- <tr>
- <td>@forecast.Id</td>
- <td>@forecast.Name</td>
- <td>@forecast.Price</td>
- </tr>
- }
- </tbody>
- </table>
- }
創(chuàng)建一個(gè)代碼隱藏文件 Products.razor.cs,并將配置的 HttpClient 實(shí)例作為私有成員注入到該類中。最后,使用 GetFromJsonAsync 方法調(diào)用產(chǎn)品 API。
Products.razor.cs
- public partial class Products
- {
- private List<Product> products;
- [Inject]
- private HttpClient Http { get; set; }
- protected override async Task OnInitializedAsync()
- {
- products = await Http.GetFromJsonAsync<List<Product>>("products");
- }
- }
您還需要在 Blazor WebAssembly 項(xiàng)目中創(chuàng)建一個(gè) Product 類的本地副本,以將產(chǎn)品 API 的結(jié)果反序列化為產(chǎn)品對(duì)象列表。
- public class Product
- {
- public int Id { get; set; }
- public string Name { get; set; }
- public decimal Price { get; set; }
- }
運(yùn)行該項(xiàng)目,您將看到從后端 Web API 加載了產(chǎn)品的頁(yè)面。