共用方式為


使用 HttpClient 類別提出 HTTP 要求

在本文中,您將瞭解如何使用 HttpClient 類別提出 HTTP 要求及處理回應。

重要

本文中的所有範例 HTTP 要求都會以下列其中一個 URL 為目標:

HTTP 端點通常會傳回 JavaScript 物件標記法 (JSON) 資料,但不一定都是如此。 為了方便起見,選擇性 System.Net.Http.Json NuGet 套件提供數個擴充方法,讓 HttpClientHttpContent 物件使用 📦 System.Text.Json NuGet 套件來執行自動串行化和還原串行化。 此文章中的範例會強調這些擴充功能可供使用的位置。

提示

本文中參考的所有原始程式碼都可在 GitHub:.NET Docs 儲存庫 中取得。

建立 HttpClient 物件

本文中的大部分範例會重複使用相同的 HttpClient 實例,因此您可以設定實例一次,並將其用於其餘範例。 若要建立 HttpClient 物件,請使用 HttpClient 類別建構函式。 如需詳細資訊,請參閱使用 HttpClient 的指導方針

// HttpClient lifecycle management best practices:
// https://learn.microsoft.com/dotnet/fundamentals/networking/http/httpclient-guidelines#recommended-use
private static HttpClient sharedClient = new()
{
    BaseAddress = new Uri("https://jsonplaceholder.typicode.com"),
};

程式代碼會完成下列工作:

  • 將新的 HttpClient 實例具現化為 static 變數。 根據 指導方針,建議的方法是在應用程式生命週期期間重複使用 HttpClient 實例。
  • HttpClient.BaseAddress 屬性設定為 "https://jsonplaceholder.typicode.com"

這個 HttpClient 實例會使用基位址來提出後續要求。 若要套用其他組態,請考慮下列 API:

提示

或者,您可以使用工廠模式方法來建立 HttpClient 實例,以便配置任意數量的用戶端,並將其作為依賴注入服務來取用。 如需詳細資訊,請參閱使用 .NET 的 HTTP 用戶端處理站

提出 HTTP 要求

若要提出 HTTP 要求,您可以呼叫下列任何 API 方法:

HTTP 方法 API(應用程式介面)
GET HttpClient.GetAsync
GET HttpClient.GetByteArrayAsync
GET HttpClient.GetStreamAsync
GET HttpClient.GetStringAsync
POST HttpClient.PostAsync
PUT HttpClient.PutAsync
PATCH HttpClient.PatchAsync
DELETE HttpClient.DeleteAsync
USER SPECIFIED HttpClient.SendAsync

一個 USER SPECIFIED 請求表示 SendAsync 方法可以接受任何有效的 HttpMethod 物件。

警告

提出 HTTP 要求會視為網路 I/O 繫結工作。 同步 HttpClient.Send 方法存在,但建議您改用異步 API,除非您有充分的理由不要這樣做。

注意

雖然以 Android 裝置為目標(例如使用 .NET MAUI 開發),但您必須將 android:usesCleartextTraffic="true" 定義新增至 <application></application> 檔案中的 區段。 此設定會啟用明文流量,例如 HTTP 請求,否則預設根據 Android 安全策略會被停用。 請考慮下列範例 XML 設定:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <application android:usesCleartextTraffic="true"></application>
  <!-- omitted for brevity -->
</manifest>

如需詳細資訊,請參閱啟用本地主機網域的純文字網路流量

瞭解 HTTP 內容

HttpContent 類型用於表示 HTTP 實體內容和對應的內容標頭。 針對需要主體的 HTTP 方法(或要求方法)(POSTPUTPATCH),您可以使用 HttpContent 類別來指定要求的主體。 大部分範例都會示範如何使用 JSON 承載來準備 StringContent 子類別,但不同內容 (MIME) 型別會有其他子類別。

HttpContent 類別也用來表示 HttpResponseMessage 類別的響應主體,該類別可在 HttpResponseMessage.Content 屬性上存取。

使用 HTTP GET 請求

GET 要求不應該傳送主體。 此請求如其名稱所示,用來從資源中檢索(或取得)資料。 若要在指定 GET 實例和 HttpClient 對象的情況下提出 HTTP Uri 要求,請使用 HttpClient.GetAsync 方法:

static async Task GetAsync(HttpClient httpClient)
{
    using HttpResponseMessage response = await httpClient.GetAsync("todos/3");
    
    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();
    
    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output:
    //   GET https://jsonplaceholder.typicode.com/todos/3 HTTP/1.1
    //   {
    //     "userId": 1,
    //     "id": 3,
    //     "title": "fugiat veniam minus",
    //     "completed": false
    //   }
}

程式代碼會完成下列工作:

  • GET 端點提出 "https://jsonplaceholder.typicode.com/todos/3" 要求。
  • 請確定回應成功。
  • 將要求詳細數據寫入主控台。
  • 將回應本文讀取為字串。
  • 將 JSON 回應內容寫入主控台。

WriteRequestToConsole 方法是不屬於架構的自定義延伸模組。 如果您好奇實作,請考慮下列 C# 程序代碼:

static class HttpResponseMessageExtensions
{
    internal static void WriteRequestToConsole(this HttpResponseMessage response)
    {
        if (response is null)
        {
            return;
        }

        var request = response.RequestMessage;
        Console.Write($"{request?.Method} ");
        Console.Write($"{request?.RequestUri} ");
        Console.WriteLine($"HTTP/{request?.Version}");        
    }
}

這項功能可用來以下列形式將要求詳細資料寫入主控台:

<HTTP Request Method> <Request URI> <HTTP/Version>

例如,對 GET 端點的 "https://jsonplaceholder.typicode.com/todos/3" 要求會輸出下列訊息:

GET https://jsonplaceholder.typicode.com/todos/3 HTTP/1.1

從 JSON 建立 HTTP GET 要求

https://jsonplaceholder.typicode.com/todos 端點會傳回 Todo 物件的 JSON 陣列。 其 JSON 結構類似下列形式:

[
  {
    "userId": 1,
    "id": 1,
    "title": "example title",
    "completed": false
  },
  {
    "userId": 1,
    "id": 2,
    "title": "another example title",
    "completed": true
  },
]

C# Todo 物件的定義如下:

public record class Todo(
    int? UserId = null,
    int? Id = null,
    string? Title = null,
    bool? Completed = null);

其型別為 record class,具有選擇性的 IdTitleCompletedUserId 屬性。 如需 record 型別的詳細資訊,請參閱 C# 中的記錄型別簡介。 若要將 GET 要求自動反序列化為強型別 C# 物件,請使用屬於 📦 NuGet 套件的一部分的 擴充方法。

static async Task GetFromJsonAsync(HttpClient httpClient)
{
    var todos = await httpClient.GetFromJsonAsync<List<Todo>>(
        "todos?userId=1&completed=false");

    Console.WriteLine("GET https://jsonplaceholder.typicode.com/todos?userId=1&completed=false HTTP/1.1");
    todos?.ForEach(Console.WriteLine);
    Console.WriteLine();

    // Expected output:
    //   GET https://jsonplaceholder.typicode.com/todos?userId=1&completed=false HTTP/1.1
    //   Todo { UserId = 1, Id = 1, Title = delectus aut autem, Completed = False }
    //   Todo { UserId = 1, Id = 2, Title = quis ut nam facilis et officia qui, Completed = False }
    //   Todo { UserId = 1, Id = 3, Title = fugiat veniam minus, Completed = False }
    //   Todo { UserId = 1, Id = 5, Title = laboriosam mollitia et enim quasi adipisci quia provident illum, Completed = False }
    //   Todo { UserId = 1, Id = 6, Title = qui ullam ratione quibusdam voluptatem quia omnis, Completed = False }
    //   Todo { UserId = 1, Id = 7, Title = illo expedita consequatur quia in, Completed = False }
    //   Todo { UserId = 1, Id = 9, Title = molestiae perspiciatis ipsa, Completed = False }
    //   Todo { UserId = 1, Id = 13, Title = et doloremque nulla, Completed = False }
    //   Todo { UserId = 1, Id = 18, Title = dolorum est consequatur ea mollitia in culpa, Completed = False }
}

程式代碼會完成下列工作:

  • GET提出 "https://jsonplaceholder.typicode.com/todos?userId=1&completed=false" 請求。

    查詢字串代表要求的篩選準則。 當命令成功時,回應會自動還原串行化為 List<Todo> 物件。

  • 將請求詳細資訊寫入主控台,並包括每個 Todo 物件。

使用 HTTP POST 請求

POST 要求會將資料傳送至伺服器進行處理。 要求的 Content-Type 標頭表示本文正在傳送的 MIME 型別。 若要在指定 POST 實例和 HttpClient 對象的情況下提出 HTTP Uri 要求,請使用 HttpClient.PostAsync 方法:

static async Task PostAsync(HttpClient httpClient)
{
    using StringContent jsonContent = new(
        JsonSerializer.Serialize(new
        {
            userId = 77,
            id = 1,
            title = "write code sample",
            completed = false
        }),
        Encoding.UTF8,
        "application/json");

    using HttpResponseMessage response = await httpClient.PostAsync(
        "todos",
        jsonContent);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();
    
    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output:
    //   POST https://jsonplaceholder.typicode.com/todos HTTP/1.1
    //   {
    //     "userId": 77,
    //     "id": 201,
    //     "title": "write code sample",
    //     "completed": false
    //   }
}

程式代碼會完成下列工作:

  • 使用要求的 JSON 主體來準備 StringContent 例項(MIME 類型為 "application/json")。
  • POST 端點提出 "https://jsonplaceholder.typicode.com/todos" 要求。
  • 請確定回應成功,並將要求詳細數據寫入主控台。
  • 將回應內容作為字串輸出到主控台。

以 JSON 格式建立 HTTP POST 要求

若要自動序列化 POST 要求引數,並將回應還原序列化為強型別 C# 物件,請使用屬於 PostAsJsonAsync NuGet 套件的 擴充方法。

static async Task PostAsJsonAsync(HttpClient httpClient)
{
    using HttpResponseMessage response = await httpClient.PostAsJsonAsync(
        "todos", 
        new Todo(UserId: 9, Id: 99, Title: "Show extensions", Completed: false));

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    var todo = await response.Content.ReadFromJsonAsync<Todo>();
    Console.WriteLine($"{todo}\n");

    // Expected output:
    //   POST https://jsonplaceholder.typicode.com/todos HTTP/1.1
    //   Todo { UserId = 9, Id = 201, Title = Show extensions, Completed = False }
}

程式代碼會完成下列工作:

  • Todo 實例串行化為 JSON,並對 POST 端點提出 "https://jsonplaceholder.typicode.com/todos" 要求。
  • 請確定回應成功,並將要求詳細數據寫入主控台。
  • 將回應本文反序列化為 Todo 實例,並將 Todo 物件寫入控制台。

使用 HTTP PUT 請求

PUT 要求方法會取代現有的資源,或使用請求內容建立新的資源。 若要在指定 PUT 實例和 HttpClient 對象的情況下提出 HTTP Uri 要求,請使用 HttpClient.PutAsync 方法:

static async Task PutAsync(HttpClient httpClient)
{
    using StringContent jsonContent = new(
        JsonSerializer.Serialize(new 
        {
            userId = 1,
            id = 1,
            title = "foo bar",
            completed = false
        }),
        Encoding.UTF8,
        "application/json");

    using HttpResponseMessage response = await httpClient.PutAsync(
        "todos/1",
        jsonContent);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();
    
    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output:
    //   PUT https://jsonplaceholder.typicode.com/todos/1 HTTP/1.1
    //   {
    //     "userId": 1,
    //     "id": 1,
    //     "title": "foo bar",
    //     "completed": false
    //   }
}

程式代碼會完成下列工作:

  • 使用要求的 JSON 主體來準備 StringContent 例項(MIME 類型為 "application/json")。
  • PUT 端點提出 "https://jsonplaceholder.typicode.com/todos/1" 要求。
  • 請確保回應成功與否,並將請求詳細資訊及 JSON 回應正文寫入主控台。

建立 HTTP PUT 要求以 JSON 格式

若要自動序列化 PUT 要求引數,並將回應還原序列化為強型別 C# 物件,請使用屬於 PutAsJsonAsync NuGet 套件的 擴充方法。

static async Task PutAsJsonAsync(HttpClient httpClient)
{
    using HttpResponseMessage response = await httpClient.PutAsJsonAsync(
        "todos/5",
        new Todo(Title: "partially update todo", Completed: true));

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    var todo = await response.Content.ReadFromJsonAsync<Todo>();
    Console.WriteLine($"{todo}\n");

    // Expected output:
    //   PUT https://jsonplaceholder.typicode.com/todos/5 HTTP/1.1
    //   Todo { UserId = , Id = 5, Title = partially update todo, Completed = True }
}

程式代碼會完成下列工作:

  • Todo 實例串行化為 JSON,並對 PUT 端點提出 "https://jsonplaceholder.typicode.com/todos/5" 要求。
  • 請確定回應成功,並將要求詳細數據寫入主控台。
  • 將回應本文反序列化為 Todo 實例,並將 Todo 物件寫入控制台。

使用 HTTP PATCH 請求

PATCH 請求是對現有資源的部分更新。 此要求不會建立新的資源,也不會用來取代現有的資源。 相反地,這個方法只會部分更新資源。 若要在指定 PATCH 實例和 HttpClient 對象的情況下提出 HTTP Uri 要求,請使用 HttpClient.PatchAsync 方法:

static async Task PatchAsync(HttpClient httpClient)
{
    using StringContent jsonContent = new(
        JsonSerializer.Serialize(new
        {
            completed = true
        }),
        Encoding.UTF8,
        "application/json");

    using HttpResponseMessage response = await httpClient.PatchAsync(
        "todos/1",
        jsonContent);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output
    //   PATCH https://jsonplaceholder.typicode.com/todos/1 HTTP/1.1
    //   {
    //     "userId": 1,
    //     "id": 1,
    //     "title": "delectus aut autem",
    //     "completed": true
    //   }
}

程式代碼會完成下列工作:

  • 使用要求的 JSON 主體來準備 StringContent 例項(MIME 類型為 "application/json")。
  • PATCH 端點提出 "https://jsonplaceholder.typicode.com/todos/1" 要求。
  • 請確保回應成功與否,並將請求詳細資訊及 JSON 回應正文寫入主控台。

PATCH NuGet 套件中沒有 System.Net.Http.Json 要求的擴充方法。

使用 HTTP DELETE 要求

DELETE 請求會移除現有的資源,而且請求是 等冪的,但不是 安全的。 對相同資源的多個 DELETE 要求會產生相同的結果,但要求會影響資源的狀態。 若要在指定 DELETE 實例和 HttpClient 對象的情況下提出 HTTP Uri 要求,請使用 HttpClient.DeleteAsync 方法:

static async Task DeleteAsync(HttpClient httpClient)
{
    using HttpResponseMessage response = await httpClient.DeleteAsync("todos/1");
    
    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output
    //   DELETE https://jsonplaceholder.typicode.com/todos/1 HTTP/1.1
    //   {}
}

程式代碼會完成下列工作:

  • DELETE 端點提出 "https://jsonplaceholder.typicode.com/todos/1" 要求。
  • 請確定回應成功,並將要求詳細數據寫入主控台。

提示

DELETE 要求的回應(就像 PUT 要求一樣),可能會或可能不會包含主體。

探索 HTTP HEAD 要求

HEAD 要求類似於 GET 要求。 此要求只會傳回與資源相關聯的標頭,而不是傳回資源。 HEAD 要求的回應沒有內容。 若要在指定 HEAD 實例和 HttpClient 對象的情況下提出 HTTP Uri 要求,請使用 HttpClient.SendAsync 方法,並將 HttpMethod 類型設定為 HttpMethod.Head

static async Task HeadAsync(HttpClient httpClient)
{
    using HttpRequestMessage request = new(
        HttpMethod.Head, 
        "https://www.example.com");

    using HttpResponseMessage response = await httpClient.SendAsync(request);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    foreach (var header in response.Headers)
    {
        Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
    }
    Console.WriteLine();

    // Expected output:
    //   HEAD https://www.example.com/ HTTP/1.1
    //   Accept-Ranges: bytes
    //   Age: 550374
    //   Cache-Control: max-age=604800
    //   Date: Wed, 10 Aug 2022 17:24:55 GMT
    //   ETag: "3147526947"
    //   Server: ECS, (cha / 80E2)
    //   X-Cache: HIT
}

程式代碼會完成下列工作:

  • HEAD 端點提出 "https://www.example.com/" 要求。
  • 請確定回應成功,並將要求詳細數據寫入主控台。
  • 逐一查看所有響應標頭,並將每個標頭寫入主控台。

研究 HTTP OPTIONS 請求

OPTIONS 要求會用來識別伺服器或端點支援的 HTTP 方法。 若要在指定 OPTIONS 實例和 HttpClient 對象的情況下提出 HTTP Uri 要求,請使用 HttpClient.SendAsync 方法,並將 HttpMethod 類型設定為 HttpMethod.Options

static async Task OptionsAsync(HttpClient httpClient)
{
    using HttpRequestMessage request = new(
        HttpMethod.Options, 
        "https://www.example.com");

    using HttpResponseMessage response = await httpClient.SendAsync(request);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    foreach (var header in response.Content.Headers)
    {
        Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
    }
    Console.WriteLine();

    // Expected output
    //   OPTIONS https://www.example.com/ HTTP/1.1
    //   Allow: OPTIONS, GET, HEAD, POST
    //   Content-Type: text/html; charset=utf-8
    //   Expires: Wed, 17 Aug 2022 17:28:42 GMT
    //   Content-Length: 0
}

程式代碼會完成下列工作:

  • OPTIONS HTTP 要求傳送至 "https://www.example.com/" 端點。
  • 請確定回應成功,並將要求詳細數據寫入主控台。
  • 逐一查看所有響應內容標頭,並將每個標頭寫入主控台。

探索 HTTP TRACE 要求

TRACE 要求對於偵錯很有用,因為其提供要求訊息的應用層級回送。 若要發出 HTTP TRACE 要求,請透過使用 HttpRequestMessage 類型來建立 HttpMethod.Trace

using HttpRequestMessage request = new(
    HttpMethod.Trace, 
    "{ValidRequestUri}");

警告

並非所有 HTTP 伺服器都支援 TRACE HTTP 方法。 如果不明智地使用,這個方法可能會公開安全性弱點。 如需詳細資訊,請參閱 開放 Web 應用程式安全專案 (OWASP):跨網站追蹤

處理 HTTP 回應

當您處理 HTTP 回應時,您會與 HttpResponseMessage 類型互動。 數個成員可用來評估回應的有效性。 HTTP 狀態代碼可在 HttpResponseMessage.StatusCode 屬性中使用。

假設您針對特定客戶端實例發送請求:

using HttpResponseMessage response = await httpClient.SendAsync(request);

若要確保 responseOK(HTTP 狀態代碼 200),您可以評估其值,如下列範例所示:

if (response is { StatusCode: HttpStatusCode.OK })
{
    // Omitted for brevity...
}

還有其他代表回應成功的 HTTP 狀態碼,例如 CREATED (HTTP 狀態碼 201)、ACCEPTED (HTTP 狀態碼 202)、NO CONTENT (HTTP 狀態碼 204),以及 RESET CONTENT (HTTP 狀態碼 205)。 您也可以使用 HttpResponseMessage.IsSuccessStatusCode 屬性來評估這些代碼,以確保回應狀態碼在 200-299 範圍內:

if (response.IsSuccessStatusCode)
{
    // Omitted for brevity...
}

如果您需要讓架構擲回 HttpRequestException 錯誤,您可以呼叫 HttpResponseMessage.EnsureSuccessStatusCode() 方法:

response.EnsureSuccessStatusCode();

如果回應狀態代碼不在 200-299 範圍內,則此程式代碼會擲回 HttpRequestException 錯誤。

探索 HTTP 有效內容回應

透過有效的回應,您可以使用 Content 屬性來存取回應主體。 內文可作為 HttpContent 實例使用,您可以使用此實例來存取內文做為數據流、位元組陣列或字串。

下列程式碼會使用 responseStream 物件來讀取回應的主體。

await using Stream responseStream =
    await response.Content.ReadAsStreamAsync();

您可以使用不同的對象來讀取回應主體。 使用 responseByteArray 對象來讀取回應本文:

byte[] responseByteArray = await response.Content.ReadAsByteArrayAsync();

使用 responseString 對象來讀取回應本文:

string responseString = await response.Content.ReadAsStringAsync();

當您知道 HTTP 端點傳回 JSON 時,您可以使用 System.Net.Http.Json NuGet 套件,將回應本文還原串行化為任何有效的 C# 物件:

T? result = await response.Content.ReadFromJsonAsync<T>();

在此程式代碼中,result 值是還原串行化為類型 T的響應主體。

使用 HTTP 錯誤處理

當 HTTP 要求失敗時,系統會擲回 HttpRequestException 物件。 可能僅捕捉例外狀況就不夠。 您可能想要考慮處理其他可能發生的例外狀況。 例如,呼叫代碼可能會使用在請求完成之前即已被取消的取消令牌。 在此情境中,您可以捕捉 TaskCanceledException 錯誤:

using var cts = new CancellationTokenSource();
try
{
    // Assuming:
    //   httpClient.Timeout = TimeSpan.FromSeconds(10)

    using var response = await httpClient.GetAsync(
        "http://localhost:5001/sleepFor?seconds=100", cts.Token);
}
catch (OperationCanceledException ex) when (cts.IsCancellationRequested)
{
    // When the token has been canceled, it is not a timeout.
    Console.WriteLine($"Canceled: {ex.Message}");
}

同樣地,當您提出 HTTP 要求時,如果伺服器未在超過 HttpClient.Timeout 值之前回應,則會擲回相同的例外狀況。 在此情境中,當攔截 Exception.InnerException 錯誤時,您可以透過評估 TaskCanceledException 屬性來識別逾時的發生:

try
{
    // Assuming:
    //   httpClient.Timeout = TimeSpan.FromSeconds(10)

    using var response = await httpClient.GetAsync(
        "http://localhost:5001/sleepFor?seconds=100");
}
catch (OperationCanceledException ex) when (ex.InnerException is TimeoutException tex)
{
    Console.WriteLine($"Timed out: {ex.Message}, {tex.Message}");
}

在程式代碼中,當內部例外狀況是 TimeoutException 類型時,就會發生逾時,而且取消令牌不會取消要求。

若要在攔截 HttpRequestException 物件時評估 HTTP 狀態代碼,您可以評估 HttpRequestException.StatusCode 屬性:

try
{
    // Assuming:
    //   httpClient.Timeout = TimeSpan.FromSeconds(10)

    using var response = await httpClient.GetAsync(
        "http://localhost:5001/doesNotExist");

    response.EnsureSuccessStatusCode();
}
catch (HttpRequestException ex) when (ex is { StatusCode: HttpStatusCode.NotFound })
{
    // Handle 404
    Console.WriteLine($"Not found: {ex.Message}");
}

在程序代碼中,呼叫 EnsureSuccessStatusCode() 方法,以在回應未成功時擲回例外狀況。 接著會評估 HttpRequestException.StatusCode 屬性,以判斷回應是否為 404 (HTTP 狀態碼 404)。 HttpClient 物件上有數個輔助方法,會自動代表您呼叫 EnsureSuccessStatusCode 方法。

針對 HTTP 錯誤處理,請考慮下列 API:

提示

所有用於發出 HTTP 請求的 HttpClient 方法,在不回傳 HttpResponseMessage 類型的情況下,會代表您隱含地呼叫 EnsureSuccessStatusCode 方法。

當您呼叫這些方法時,您可以處理 HttpRequestException 物件,並評估 HttpRequestException.StatusCode 屬性,以判斷回應的 HTTP 狀態代碼:

try
{
    // These extension methods will throw HttpRequestException
    // with StatusCode set when the HTTP request status code isn't 2xx:
    //
    //   GetByteArrayAsync
    //   GetStreamAsync
    //   GetStringAsync

    using var stream = await httpClient.GetStreamAsync(
        "https://localhost:5001/doesNotExists");
}
catch (HttpRequestException ex) when (ex is { StatusCode: HttpStatusCode.NotFound })
{
    // Handle 404
    Console.WriteLine($"Not found: {ex.Message}");
}

在某些情況下,您可能需要在程式碼中拋出 HttpRequestException 物件。 HttpRequestException() 建構函式是公用的,您可以使用它來擲回具有自定義訊息的例外狀況:

try
{
    using var response = await httpClient.GetAsync(
        "https://localhost:5001/doesNotExists");

    // Throw for anything higher than 400.
    if (response is { StatusCode: >= HttpStatusCode.BadRequest })
    {
        throw new HttpRequestException(
            "Something went wrong", inner: null, response.StatusCode);
    }
}
catch (HttpRequestException ex) when (ex is { StatusCode: HttpStatusCode.NotFound })
{
    Console.WriteLine($"Not found: {ex.Message}");
}

設定 HTTP 代理

HTTP Proxy 可以透過下列兩種方式之一來設定。 HttpClient.DefaultProxy 屬性上會指定預設值。 或者,您可以在 HttpClientHandler.Proxy 屬性上指定 Proxy。

使用全域預設 Proxy

HttpClient.DefaultProxy 屬性是靜態屬性,用於決定所有 HttpClient 實例使用的預設 proxy,如果在透過其構造函數傳遞進去的 HttpClientHandler 對象中未設定 proxy。

此屬性傳回預設實例會根據平臺的不同規則集初始化:

  • Windows:從環境變數讀取 Proxy 組態,或如果未定義變數,請從使用者 Proxy 設定讀取。
  • macOS:從環境變數讀取 Proxy 組態,或如果未定義變數,請從系統 Proxy 設定讀取。
  • Linux:從環境變數讀取 Proxy 組態,或如果未定義變數,請初始化未設定的實例以略過所有位址。

Windows 和 Unix 平臺上 DefaultProxy 屬性初始化會使用下列環境變數:

  • HTTP_PROXY:用於 HTTP 要求的 Proxy 伺服器。
  • HTTPS_PROXY:在 HTTPS 要求上使用的 Proxy 伺服器。
  • ALL_PROXY:未定義 HTTP_PROXY 和/或 HTTPS_PROXY 變數時,HTTP 和/或 HTTPS 要求上使用的 Proxy 伺服器。
  • NO_PROXY:要從 Proxy 中排除的由逗號分隔的主機名稱清單。 星號不支援用作通配符。 當您想要匹配子域時,請使用前面的句點(.)。 範例:NO_PROXY=.example.com(具有前置句點)匹配 www.example.com,但不匹配 example.comNO_PROXY=example.com(沒有前置句點)不符合 www.example.com。 未來可能會重新檢視此行為,以更符合其他生態系統。

在環境變數區分大小寫的系統上,變數名稱可以是小寫或全部大寫。 系統會先檢查小寫名稱。

Proxy 伺服器可以是主機名或IP位址,選擇性地後面接著冒號和埠號碼,也可以是 http URL,選擇性地包含 Proxy 驗證的使用者名稱和密碼。 URL 必須以 http開頭,而不是 https,而且不能在主機名、IP 或埠之後包含任何文字。

設定每個用戶端的 Proxy

HttpClientHandler.Proxy 屬性會識別用來處理因特網資源要求 WebProxy 物件。 若要指定不應使用 Proxy,請將 Proxy 屬性設定為 GlobalProxySelection.GetEmptyWebProxy() 方法所傳回的 Proxy 執行個體。

本機電腦或應用程式組態檔可能會指定使用預設 Proxy。 如果指定了 Proxy 屬性,則來自 Proxy 屬性的 Proxy 設定會覆寫本機電腦或應用程式組態檔,而處理程式會使用指定的 Proxy 設定。 如果未指定組態檔中的 Proxy,且未指定 Proxy 屬性,處理程式會使用繼承自本機計算機的 Proxy 設定。 如果沒有 Proxy 設定,則會將要求直接傳送至伺服器。

HttpClientHandler 類別會解析具有萬用字元的 Proxy 略過清單,該清單繼承自本機電腦設定。 例如,HttpClientHandler 類別會將來自瀏覽器的 "nt*" 迴避清單解析為 "nt.*" 的正則表達式。 因此,http://nt.com 的 URL 會使用 HttpClientHandler 類別略過 Proxy。

HttpClientHandler 類別支援本地 Proxy 略過。 如果符合下列任何條件,該類別會將目的地視為本地:

  • 目的地包含 URL 中的簡化名稱(沒有句號(.))。
  • 目的地包含回送位址(LoopbackIPv6Loopback),或目的地包含指派給本機計算機的 IPAddress 屬性。
  • 目的地的網域後綴符合本機計算機的網域後綴,如 DomainName 屬性中所定義。

如需設定 Proxy 的詳細資訊,請參閱下列 API:

後續步驟