從 .NET 用戶端呼叫 Web API (C#)
此內容適用於 .NET 的早期版本。 新開發應該使用 ASP.NET Core。 有關使用 ASP.NET Core Web API 的更多資訊,請參閱:
本教學課程示範如何使用 System.Net.Http.HttpClient 從 .NET 應用程式呼叫 Web API。
在本教學課程中,會編寫一個用戶端應用程式,來取用以下的 Web API:
動作 | HTTP method (HTTP 方法) | Relative URI |
---|---|---|
依照識別碼取得產品 | 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 Store 應用程式也支援 HttpClient。 有關更多資訊,請參閱「使用可攜式程式庫為多個平台編寫 Web API 用戶端程式碼」
注意:如果您將基底 URL 和相對 URI 作為硬式編碼值傳遞,請注意使用 HttpClient
API 的規則。 HttpClient.BaseAddress
屬性應設定為結尾帶有斜線 (/
) 的位址。 例如,將硬式編碼資源 URI 傳遞給 HttpClient.GetAsync
方法時,請勿包含前置斜線。 透過識別碼取得 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
內完成。 通常應用程式不會阻塞主執行緒,但此應用程式不允許任何互動。
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。 如果狀態代碼超出 200–299 範圍,則 EnsureSuccessStatusCode
會擲回例外狀況。 請注意,HttpClient 可能會因其他原因擲回例外狀況,例如,如果要求逾時。
要還原序列化的媒體類型格式器
當不帶參數呼叫 ReadAsAsync 時,它使用一組預設的媒體格式器來讀取回應本文。 預設格式器支援 JSON、XML 和 Form-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 要求以建立資源
以下程式碼會傳送包含 Product
執行個體 (JSON 格式) 的 POST 要求:
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 (已建立) 回應。
- 回應應在 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 要求以刪除資源
以下程式碼會傳送 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)