共用方式為


從 .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

  1. 設定 client.BaseAddress = new Uri("https://localhost:5001/");
  2. 要求 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 格式。

測試範例

若要測試用戶端應用程式:

  1. 下載並執行伺服器應用程式。 確認伺服器應用程式是否正常運作。 例如,http://localhost:64195/api/products 應傳回產品清單。

  2. 設定 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"));
    
  3. 執行用戶端應用程式。 此時會產生下列輸出:

    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)