在我的前篇文章《Blazor Server 应用程序中进行 HTTP 请求》中,我介绍了在 Blazor Server 应用程序中进行 HTTP 请求的相关技术,在 Blazor Server App 中您可以访问所有的 .NET 类库和组件。但如果您创建的是 Blazor WebAssembly 应用程序,那么您的代码将在客户端的浏览器沙箱中运行,您的选择在某种程度上会受到限制。在本教程中,我将向您展示如何在 Blazor WebAssembly 应用程序进行 HTTP 请求。
Blazor WebAssembly 应用程序使用预置的 HttpClient 服务调用 Web API。这个预置的 HttpClient 是使用浏览器的 Fetch API[2] 实现的,会有一些限制。HttpClient 还可以使用 Blazor JSON 帮助程序或 HttpRequestMessage 对象进行 API 调用。默认情况下,您只能向同源服务器发送 API 调用请求,不过如果第三方 API 支持跨域资源共享(CORS)的话,您也可以调用其他服务器上的 API。
命名空间 System.Net.Http.Json 为使用 System.Text.Json 执行自动序列化和反序列化的 HttpClient 提供了扩展方法。这些扩展方法将请求发送到一个 Web API URI 并处理相应的响应。常用的方法有:
GetFromJsonAsync:发送 HTTP GET 请求,并将 JSON 响应正文解析成一个对象。
PostAsJsonAsync:将 POST 请求发送到指定的 URI,并在请求正文中载有序列化为 JSON 的 value
。
PutAsJsonAsync:发送 HTTP PUT 请求,其中包含 JSON 编码的内容。
要理解如何将这些方法与 HttpClient 一起使用,我们需要创建两个项目。第一个项目是一个 Web API 项目,它向客户端公开一个 Web API。第二个项目是 Blazor WebAssembly 应用程序,它向第一个项目中创建的 Web API 发送 HTTP 请求。
在本节中,我们将实现一个支持跨域资源共享 (CORS) 的 Web API,以便 Blazor WebAssembly 应用程序可以调用此 API。在 Visual Studio 2019 中创建一个新的 Web API 项目 BlazorClientWebAPI。我们将创建一个简单的 API 来返回产品列表,所以首先要在项目中创建一个 Models 文件夹,并在其中添加如下的 Product 类。
Product.cs
public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } }
接下来,创建一个 Controllers 文件夹并在其中添加下面的 ProductsController。该控制器简单地从 GetProducts 方法返回一些模拟的产品数据。
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); } }
现在如果您运行该项目,并尝试在浏览器中使用 URI api/products 访问该 API,您应该能看到以 JSON 格式返回的产品数据。
默认情况下,浏览器安全性不允许一个网页向除提供该网页的域之外的其他域发送请求。这种约束称之为同源策略。如果我们希望 Blazor WebAssembly 应用程序或其他客户端应用程序使用上述 Web API,那么我们必须启用跨域资源共享 (CORS)。打开 Startup.cs 文件,并在 ConfigureServices 方法中调用 AddCors 方法。
public void ConfigureServices(IServiceCollection services) { services.AddCors(policy => { policy.AddPolicy("CorsPolicy", opt => opt .AllowAnyOrigin() .AllowAnyHeader() .AllowAnyMethod()); }); services.AddControllers(); }
同时,在 Startup.cs 文件的 Configure 方法中添加以下代码行。
app.UseCors("CorsPolicy");
有关使用 ASP.NET Core 应用程序的 CORS 的详细信息,请参阅 《Enable Cross-Origin Requests (CORS) in ASP.NET Core》[3]。
在创建上述 Web API 项目的同一解决方案中添加一个新的 Blazor WebAssembly 应用程序项目 BlazorClientWebAPIsDemo。
我们需要确保的第一件事是,在项目文件中有 System.Net.Http.Json 的引用。如果没有,那么您可以添加该引用。
<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>
接下来,我们需要在 Program.cs 文件中配置 HttpClient 服务。确保提供了要从 Blazor WebAssembly 应用程序调用的 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(); }
为了使用产品 API,我们在 Pages 文件夹中创建一个 Products.razor 组件。该视图非常简单,因为它只是迭代产品列表并使用简单的 HTML 表格来显示它们。
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> }
创建一个代码隐藏文件 Products.razor.cs,并将配置的 HttpClient 实例作为私有成员注入到该类中。最后,使用 GetFromJsonAsync 方法调用产品 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 项目中创建一个 Product 类的本地副本,以将产品 API 的结果反序列化为产品对象列表。
public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } }
运行该项目,您将看到从后端 Web API 加载了产品的页面。