C#封裝HttpClient實現(xiàn)HTTP請求處理
在現(xiàn)代的.NET應(yīng)用程序開發(fā)中,與外部服務(wù)進行HTTP通信是一項常見需求。HttpClient作為.NET框架中處理HTTP請求的核心組件,為我們提供了強大而靈活的API。然而,直接使用原生的HttpClient可能會導(dǎo)致代碼重復(fù)、錯誤處理不完善等問題。為了提高代碼的可維護性和可測試性,我們通常會對HttpClient進行封裝。本文將介紹一個完整的HttpRequest類封裝實現(xiàn),并深入探討HTTP請求處理的最佳實踐。
一、完整的HttpRequest類實現(xiàn)
首先,讓我們來看一下完整的HttpRequest類實現(xiàn)代碼:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
???????public class Response
{
public bool Success { get; set; }
public string Message { get; set; }
public object Data { get; set; }
public HttpStatusCode StatusCode { get; set; }
}
public static class JsonConverterExtensions
{
public static readonly JsonSerializerOptions SerializerSettings = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
IgnoreNullValues = true,
WriteIndented = false
};
}
public class HttpRequest : IDisposable
{
private readonly HttpClient client;
private bool disposed = false;
public HttpRequest(HttpClient client)
{
this.client = client ?? throw new ArgumentNullException(nameof(client));
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
client?.Dispose();
}
disposed = true;
}
}
public async Task<Response> GetAsync(string resource)
{
try
{
var response = await client.GetAsync(resource);
return await ProcessResponseAsync(response);
}
catch (HttpRequestException ex)
{
return HandleException(ex);
}
catch (Exception ex)
{
return HandleUnexpectedException(ex);
}
}
public async Task<Response> PostAsync(string resource, object body)
{
try
{
var content = CreateJsonContent(body);
var response = await client.PostAsync(resource, content);
return await ProcessResponseAsync(response);
}
catch (HttpRequestException ex)
{
return HandleException(ex);
}
catch (Exception ex)
{
return HandleUnexpectedException(ex);
}
}
public async Task<Response> PutAsync(string resource, object body)
{
try
{
var content = CreateJsonContent(body);
var response = await client.PutAsync(resource, content);
return await ProcessResponseAsync(response);
}
catch (HttpRequestException ex)
{
return HandleException(ex);
}
catch (Exception ex)
{
return HandleUnexpectedException(ex);
}
}
public async Task<Response> DeleteAsync(string resource)
{
try
{
var response = await client.DeleteAsync(resource);
return await ProcessResponseAsync(response);
}
catch (HttpRequestException ex)
{
return HandleException(ex);
}
catch (Exception ex)
{
return HandleUnexpectedException(ex);
}
}
public HttpRequest WithBaseAddress(string baseAddress)
{
if (!string.IsNullOrEmpty(baseAddress))
{
client.BaseAddress = new Uri(baseAddress);
}
return this;
}
public HttpRequest WithTimeout(TimeSpan timeout)
{
client.Timeout = timeout;
return this;
}
public HttpRequest WithHeader(string name, string value)
{
if (!client.DefaultRequestHeaders.Contains(name))
{
client.DefaultRequestHeaders.Add(name, value);
}
return this;
}
public HttpRequest WithHeaders(IDictionary<string, string> headers)
{
if (headers != null)
{
foreach (var header in headers)
{
WithHeader(header.Key, header.Value);
}
}
return this;
}
public HttpRequest WithAuthorization(string scheme, string parameter)
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(scheme, parameter);
return this;
}
public HttpRequest WithBearerToken(string token)
{
return WithAuthorization("Bearer", token);
}
private StringContent CreateJsonContent(object body)
{
if (body == null)
{
return new StringContent("{}", Encoding.UTF8, "application/json");
}
var json = JsonSerializer.Serialize(body, JsonConverterExtensions.SerializerSettings);
return new StringContent(json, Encoding.UTF8, "application/json");
}
private async Task<Response> ProcessResponseAsync(HttpResponseMessage response)
{
var responseContent = await response.Content.ReadAsStringAsync();
try
{
// 嘗試解析JSON響應(yīng)
var responseObject = JsonSerializer.Deserialize<Response>(responseContent, JsonConverterExtensions.SerializerSettings);
if (responseObject != null)
{
responseObject.StatusCode = response.StatusCode;
return responseObject;
}
}
catch (JsonException)
{
// 如果JSON解析失敗,創(chuàng)建一個基于HTTP狀態(tài)碼的響應(yīng)
}
// 對于非JSON響應(yīng)或解析失敗的情況
return new Response
{
Success = response.IsSuccessStatusCode,
Message = response.ReasonPhrase,
StatusCode = response.StatusCode,
Data = responseContent
};
}
private Response HandleException(HttpRequestException ex)
{
return new Response
{
Success = false,
Message = $"HTTP請求錯誤: {ex.Message}",
StatusCode = ex.StatusCode ?? HttpStatusCode.InternalServerError,
Data = ex
};
}
private Response HandleUnexpectedException(Exception ex)
{
return new Response
{
Success = false,
Message = $"處理請求時發(fā)生意外錯誤: {ex.Message}",
StatusCode = HttpStatusCode.InternalServerError,
Data = ex
};
}
}
二、設(shè)計思路與實現(xiàn)要點
1. 依賴注入與生命周期管理
這個封裝類采用了依賴注入模式,通過構(gòu)造函數(shù)接收一個HttpClient實例。這樣做有幾個重要好處:
遵循單一職責(zé)原則,HttpRequest類專注于HTTP請求處理
便于單元測試,可以輕松注入模擬的HttpClient
利用.NET的IHttpClientFactory進行正確的HttpClient生命周期管理,避免資源泄漏
同時,類實現(xiàn)了IDisposable接口,確保在不再需要時正確釋放HttpClient資源。
2. 流暢接口設(shè)計
為了提供更友好的API體驗,封裝類實現(xiàn)了流暢接口模式:
var response = await new HttpRequest(httpClient)
.WithBaseAddress("https://api.example.com")
.WithBearerToken("your-token-here")
.WithHeader("X-Custom-Header", "value")
.PostAsync("/resource", new { Key = "value" });
這種鏈?zhǔn)秸{(diào)用方式使代碼更加簡潔易讀,同時保持了良好的可擴展性。
3. 統(tǒng)一的錯誤處理
在每個HTTP方法中,我們都實現(xiàn)了統(tǒng)一的異常處理機制:
- 捕獲HttpRequestException處理HTTP特定錯誤
- 捕獲其他異常處理意外錯誤
- 將所有錯誤轉(zhuǎn)換為統(tǒng)一的Response對象
- 保留原始異常信息以便調(diào)試
這種統(tǒng)一的錯誤處理方式使上層調(diào)用代碼更加簡潔,無需重復(fù)處理各種異常情況。
4. 靈活的響應(yīng)處理
ProcessResponseAsync方法負(fù)責(zé)處理HTTP響應(yīng),它嘗試將響應(yīng)內(nèi)容解析為JSON格式的Response對象:
- 如果解析成功,返回包含完整信息的Response對象
- 如果解析失敗,創(chuàng)建一個基于HTTP狀態(tài)碼的Response對象
- 始終保留原始響應(yīng)內(nèi)容和狀態(tài)碼信息
這種設(shè)計使封裝類能夠處理各種類型的HTTP響應(yīng),同時提供一致的返回格式。
三、實際使用示例
下面是一個使用這個封裝類的完整示例:
using System;
using System.Net.Http;
using System.Threading.Tasks;
???????public class Program
{
public static async Task Main()
{
try
{
// 創(chuàng)建HttpClient實例(實際應(yīng)用中建議使用IHttpClientFactory)
using var httpClient = new HttpClient();
// 創(chuàng)建請求實例并配置
var request = new HttpRequest(httpClient)
.WithBaseAddress("https://api.example.com")
.WithBearerToken("your-auth-token");
// 發(fā)送GET請求
var getResponse = await request.GetAsync("/api/users");
Console.WriteLine($"GET請求結(jié)果: {getResponse.Success}, 狀態(tài)碼: {getResponse.StatusCode}");
// 發(fā)送POST請求
var postData = new { Name = "John Doe", Email = "john@example.com" };
var postResponse = await request.PostAsync("/api/users", postData);
Console.WriteLine($"POST請求結(jié)果: {postResponse.Success}, 狀態(tài)碼: {postResponse.StatusCode}");
// 發(fā)送PUT請求
var putData = new { Id = 1, Name = "Jane Doe" };
var putResponse = await request.PutAsync("/api/users/1", putData);
Console.WriteLine($"PUT請求結(jié)果: {putResponse.Success}, 狀態(tài)碼: {putResponse.StatusCode}");
// 發(fā)送DELETE請求
var deleteResponse = await request.DeleteAsync("/api/users/1");
Console.WriteLine($"DELETE請求結(jié)果: {deleteResponse.Success}, 狀態(tài)碼: {deleteResponse.StatusCode}");
}
catch (Exception ex)
{
Console.WriteLine($"發(fā)生未處理的異常: {ex.Message}");
}
}
}
四、HttpClient使用最佳實踐
在使用HttpClient和這個封裝類時,還需要注意以下最佳實踐:
- 使用IHttpClientFactory:在ASP.NET Core應(yīng)用中,始終使用IHttpClientFactory創(chuàng)建HttpClient實例,避免直接實例化HttpClient。
- 設(shè)置合理的超時時間:默認(rèn)情況下,HttpClient的超時時間是100秒,根據(jù)實際需求調(diào)整這個值,防止長時間阻塞。
- 處理取消請求:考慮實現(xiàn)請求取消機制,通過CancellationToken參數(shù)傳遞取消令牌。
- 處理重試邏輯:對于臨時性網(wǎng)絡(luò)錯誤,考慮實現(xiàn)重試機制??梢允褂肞olly等庫來簡化重試策略的實現(xiàn)。
- 監(jiān)控HTTP請求性能:記錄HTTP請求的執(zhí)行時間、成功率等指標(biāo),便于性能分析和問題排查。
通過這個完整的HttpRequest類封裝,我們可以更加高效、安全地處理HTTP通信,同時保持代碼的整潔和可維護性。希望這篇文章對你理解C#中的HTTP請求處理有所幫助。
這個實現(xiàn)提供了完整的HTTP請求功能,包括GET、POST、PUT、DELETE方法,以及靈活的請求配置和統(tǒng)一的響應(yīng)處理。
到此這篇關(guān)于C#封裝HttpClient實現(xiàn)HTTP請求處理的文章就介紹到這了,更多相關(guān)C#封裝HttpClient內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
winform異型不規(guī)則界面設(shè)計的實現(xiàn)方法
這篇文章主要介紹了winform異型不規(guī)則界面設(shè)計的實現(xiàn)方法,具有不錯的實用價值,需要的朋友可以參考下2014-08-08
關(guān)于C#結(jié)構(gòu)體 你需要知道的
這篇文章主要介紹了關(guān)于C#結(jié)構(gòu)體的相關(guān)知識,以及使用方法,文中代碼非常詳細(xì),幫助大家更好的參考和學(xué)習(xí),感興趣的朋友可以了解下2020-06-06
解析如何正確使用SqlConnection的實現(xiàn)方法
本篇文章對如何正確使用SqlConnection的實現(xiàn)方法進行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
C#實現(xiàn)任意數(shù)據(jù)類型轉(zhuǎn)成json格式輸出
C#實現(xiàn)任意數(shù)據(jù)類型轉(zhuǎn)成json格式輸出。需要的朋友可以過來參考下,希望對大家有所幫助2013-10-10

