从 .NET 客户端调用 Web API (C#)
此内容适用于以前版本的 .NET。 新开发应使用 ASP.NET Core。 有关使用 ASP.NET Core Web API 的详细信息,请参阅:
本教程演示如何使用 System.Net.Http.HttpClient 从 .NET 应用程序调用 Web API。
在本教程中,将编写一个使用以下 Web API 的客户端应用:
操作 | HTTP 方法 | 相对 URI |
---|---|---|
根据 ID 获取产品 | GET | /api/products/id |
创建新产品 | POST | /api/products |
更新产品 | PUT | /api/products/id |
删除产品 | DELETE | /api/products/id |
若要了解如何使用 ASP.NET Web API 实现此 API,请参阅创建支持 CRUD 操作的 Web API。
为简单起见,本教程中的客户端应用程序是一个 Windows 控制台应用程序。 Windows Phone和 Windows 应用商店应用也支持 HttpClient。 有关详细信息,请参阅 使用可移植库为多个平台编写 Web API 客户端代码
注意: 如果将基 URL 和相对 URI 作为硬编码值传递,请注意使用 API 的规则 HttpClient
。 属性 HttpClient.BaseAddress
应设置为具有尾随正斜杠的 /
地址, () 。 例如,将硬编码的资源 URI 传递给 HttpClient.GetAsync
方法时,不要包含前导正斜杠。 按 ID 获取 Product
:
- 设置
client.BaseAddress = new Uri("https://localhost:5001/");
Product
请求 。 例如client.GetAsync<Product>("api/products/4");
。
创建控制台应用程序
在 Visual Studio 中,创建名为 HttpClientSample 的新 Windows 控制台应用,并粘贴以下代码:
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace HttpClientSample
{
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
class Program
{
static HttpClient client = new HttpClient();
static void ShowProduct(Product product)
{
Console.WriteLine($"Name: {product.Name}\tPrice: " +
$"{product.Price}\tCategory: {product.Category}");
}
static async Task<Uri> CreateProductAsync(Product product)
{
HttpResponseMessage response = await client.PostAsJsonAsync(
"api/products", product);
response.EnsureSuccessStatusCode();
// return URI of the created resource.
return response.Headers.Location;
}
static async Task<Product> GetProductAsync(string path)
{
Product product = null;
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
product = await response.Content.ReadAsAsync<Product>();
}
return product;
}
static async Task<Product> UpdateProductAsync(Product product)
{
HttpResponseMessage response = await client.PutAsJsonAsync(
$"api/products/{product.Id}", product);
response.EnsureSuccessStatusCode();
// Deserialize the updated product from the response body.
product = await response.Content.ReadAsAsync<Product>();
return product;
}
static async Task<HttpStatusCode> DeleteProductAsync(string id)
{
HttpResponseMessage response = await client.DeleteAsync(
$"api/products/{id}");
return response.StatusCode;
}
static void Main()
{
RunAsync().GetAwaiter().GetResult();
}
static async Task RunAsync()
{
// Update port # in the following line.
client.BaseAddress = new Uri("http://localhost:64195/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
try
{
// Create a new product
Product product = new Product
{
Name = "Gizmo",
Price = 100,
Category = "Widgets"
};
var url = await CreateProductAsync(product);
Console.WriteLine($"Created at {url}");
// Get the product
product = await GetProductAsync(url.PathAndQuery);
ShowProduct(product);
// Update the product
Console.WriteLine("Updating price...");
product.Price = 80;
await UpdateProductAsync(product);
// Get the updated product
product = await GetProductAsync(url.PathAndQuery);
ShowProduct(product);
// Delete the product
var statusCode = await DeleteProductAsync(product.Id);
Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadLine();
}
}
}
前面的代码是完整的客户端应用。
RunAsync
运行 并阻止,直到它完成。 大多数 HttpClient 方法是异步的,因为它们执行网络 I/O。 所有异步任务都在 中 RunAsync
完成。 通常,应用不会阻止main线程,但此应用不允许任何交互。
static async Task RunAsync()
{
// Update port # in the following line.
client.BaseAddress = new Uri("http://localhost:64195/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
try
{
// Create a new product
Product product = new Product
{
Name = "Gizmo",
Price = 100,
Category = "Widgets"
};
var url = await CreateProductAsync(product);
Console.WriteLine($"Created at {url}");
// Get the product
product = await GetProductAsync(url.PathAndQuery);
ShowProduct(product);
// Update the product
Console.WriteLine("Updating price...");
product.Price = 80;
await UpdateProductAsync(product);
// Get the updated product
product = await GetProductAsync(url.PathAndQuery);
ShowProduct(product);
// Delete the product
var statusCode = await DeleteProductAsync(product.Id);
Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadLine();
}
安装 Web API 客户端库
使用 NuGet 包管理器安装 Web API 客户端库包。
在“工具”菜单中,选择“NuGet 包管理器”>“包管理器控制台”。 在 PMC) (包管理器控制台中,键入以下命令:
Install-Package Microsoft.AspNet.WebApi.Client
上述命令将以下 NuGet 包添加到项目:
- Microsoft.AspNet.WebApi.Client
- Newtonsoft.Json
Newtonsoft.Json (也称为 Json.NET) 是适用于 .NET 的常用高性能 JSON 框架。
添加模型类
检查 Product
类:
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
此类与 Web API 使用的数据模型匹配。 应用可以使用 HttpClient 从 HTTP 响应中读取 Product
实例。 应用无需编写任何反序列化代码。
创建和初始化 HttpClient
检查静态 HttpClient 属性:
static HttpClient client = new HttpClient();
HttpClient 旨在实例化一次,并在应用程序的整个生命周期内重复使用。 以下条件可能会导致 SocketException 错误:
- 为每个请求创建新的 HttpClient 实例。
- 负载过重的服务器。
为每个请求创建新的 HttpClient 实例可能会耗尽可用的套接字。
以下代码初始化 HttpClient 实例:
static async Task RunAsync()
{
// Update port # in the following line.
client.BaseAddress = new Uri("http://localhost:64195/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
前面的代码:
- 设置 HTTP 请求的基 URI。 将端口号更改为服务器应用中使用的端口。 除非使用服务器应用的端口,否则应用将不起作用。
- 将 Accept 标头设置为“application/json”。 设置此标头会告知服务器以 JSON 格式发送数据。
发送 GET 请求以检索资源
以下代码发送产品的 GET 请求:
static async Task<Product> GetProductAsync(string path)
{
Product product = null;
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
product = await response.Content.ReadAsAsync<Product>();
}
return product;
}
GetAsync 方法发送 HTTP GET 请求。 方法完成后,它将返回包含 HTTP 响应的 HttpResponseMessage 。 如果响应中的状态代码是成功代码,则响应正文包含产品的 JSON 表示形式。 调用 ReadAsAsync 以将 JSON 有效负载反序列化到 Product
实例。 ReadAsAsync 方法是异步的,因为响应正文可以任意大。
当 HTTP 响应包含错误代码时,HttpClient 不会引发异常。 相反,如果状态为错误代码, 则 IsSuccessStatusCode 属性为 false 。 如果希望将 HTTP 错误代码视为异常,请在响应对象上调用 HttpResponseMessage.EnsureSuccessStatusCode 。 EnsureSuccessStatusCode
如果状态代码超出 200-299 范围,则引发异常。 请注意, HttpClient 可能会出于其他原因引发异常,例如,如果请求超时。
Media-Type格式化程序进行反序列化
在没有参数的情况下调用 ReadAsAsync 时,它将使用一组默认的 媒体格式化程序 来读取响应正文。 默认格式化程序支持 JSON、XML 和表单 URL 编码的数据。
可以向 ReadAsAsync 方法提供格式化程序列表,而不是使用默认格式化程序。 如果有自定义媒体类型格式化程序,则使用格式化程序列表非常有用:
var formatters = new List<MediaTypeFormatter>() {
new MyCustomFormatter(),
new JsonMediaTypeFormatter(),
new XmlMediaTypeFormatter()
};
resp.Content.ReadAsAsync<IEnumerable<Product>>(formatters);
有关详细信息,请参阅 ASP.NET Web API 2 中的媒体格式化程序
发送 POST 请求以创建资源
以下代码发送一个 POST 请求,其中包含 Product
JSON 格式的 实例:
static async Task<Uri> CreateProductAsync(Product product)
{
HttpResponseMessage response = await client.PostAsJsonAsync(
"api/products", product);
response.EnsureSuccessStatusCode();
// return URI of the created resource.
return response.Headers.Location;
}
PostAsJsonAsync 方法:
- 将对象序列化为 JSON。
- 在 POST 请求中发送 JSON 有效负载。
如果请求成功:
- 它应返回 201 (Created) 响应。
- 响应应在 Location 标头中包含已创建资源的 URL。
发送 PUT 请求以更新资源
以下代码发送 PUT 请求以更新产品:
static async Task<Product> UpdateProductAsync(Product product)
{
HttpResponseMessage response = await client.PutAsJsonAsync(
$"api/products/{product.Id}", product);
response.EnsureSuccessStatusCode();
// Deserialize the updated product from the response body.
product = await response.Content.ReadAsAsync<Product>();
return product;
}
PutAsJsonAsync 方法的工作方式类似于 PostAsJsonAsync,只不过它发送 PUT 请求而不是 POST。
发送删除请求以删除资源
以下代码发送 DELETE 请求以删除产品:
static async Task<HttpStatusCode> DeleteProductAsync(string id)
{
HttpResponseMessage response = await client.DeleteAsync(
$"api/products/{id}");
return response.StatusCode;
}
与 GET 一样,DELETE 请求没有请求正文。 无需使用 DELETE 指定 JSON 或 XML 格式。
测试示例
若要测试客户端应用,请执行以下操作:
下载 并运行服务器应用。 验证服务器应用是否正常工作。 例如,
http://localhost:64195/api/products
应返回产品列表。设置 HTTP 请求的基 URI。 将端口号更改为服务器应用中使用的端口。
static async Task RunAsync() { // Update port # in the following line. client.BaseAddress = new Uri("http://localhost:64195/"); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/json"));
运行客户端应用。 将生成以下输出:
Created at http://localhost:64195/api/products/4 Name: Gizmo Price: 100.0 Category: Widgets Updating price... Name: Gizmo Price: 80.0 Category: Widgets Deleted (HTTP Status = 204)