C#封裝HttpClient實(shí)現(xiàn)HTTP請(qǐng)求處理
在現(xiàn)代的.NET應(yīng)用程序開發(fā)中,與外部服務(wù)進(jìn)行HTTP通信是一項(xiàng)常見需求。HttpClient作為.NET框架中處理HTTP請(qǐng)求的核心組件,為我們提供了強(qiáng)大而靈活的API。然而,直接使用原生的HttpClient可能會(huì)導(dǎo)致代碼重復(fù)、錯(cuò)誤處理不完善等問題。為了提高代碼的可維護(hù)性和可測試性,我們通常會(huì)對(duì)HttpClient進(jìn)行封裝。本文將介紹一個(gè)完整的HttpRequest類封裝實(shí)現(xiàn),并深入探討HTTP請(qǐng)求處理的最佳實(shí)踐。
一、完整的HttpRequest類實(shí)現(xiàn)
首先,讓我們來看一下完整的HttpRequest類實(shí)現(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)建一個(gè)基于HTTP狀態(tài)碼的響應(yīng) } // 對(duì)于非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請(qǐng)求錯(cuò)誤: {ex.Message}", StatusCode = ex.StatusCode ?? HttpStatusCode.InternalServerError, Data = ex }; } private Response HandleUnexpectedException(Exception ex) { return new Response { Success = false, Message = $"處理請(qǐng)求時(shí)發(fā)生意外錯(cuò)誤: {ex.Message}", StatusCode = HttpStatusCode.InternalServerError, Data = ex }; } }
二、設(shè)計(jì)思路與實(shí)現(xiàn)要點(diǎn)
1. 依賴注入與生命周期管理
這個(gè)封裝類采用了依賴注入模式,通過構(gòu)造函數(shù)接收一個(gè)HttpClient實(shí)例。這樣做有幾個(gè)重要好處:
遵循單一職責(zé)原則,HttpRequest類專注于HTTP請(qǐng)求處理
便于單元測試,可以輕松注入模擬的HttpClient
利用.NET的IHttpClientFactory進(jìn)行正確的HttpClient生命周期管理,避免資源泄漏
同時(shí),類實(shí)現(xiàn)了IDisposable接口,確保在不再需要時(shí)正確釋放HttpClient資源。
2. 流暢接口設(shè)計(jì)
為了提供更友好的API體驗(yàn),封裝類實(shí)現(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)用方式使代碼更加簡潔易讀,同時(shí)保持了良好的可擴(kuò)展性。
3. 統(tǒng)一的錯(cuò)誤處理
在每個(gè)HTTP方法中,我們都實(shí)現(xiàn)了統(tǒng)一的異常處理機(jī)制:
- 捕獲HttpRequestException處理HTTP特定錯(cuò)誤
- 捕獲其他異常處理意外錯(cuò)誤
- 將所有錯(cuò)誤轉(zhuǎn)換為統(tǒng)一的Response對(duì)象
- 保留原始異常信息以便調(diào)試
這種統(tǒng)一的錯(cuò)誤處理方式使上層調(diào)用代碼更加簡潔,無需重復(fù)處理各種異常情況。
4. 靈活的響應(yīng)處理
ProcessResponseAsync方法負(fù)責(zé)處理HTTP響應(yīng),它嘗試將響應(yīng)內(nèi)容解析為JSON格式的Response對(duì)象:
- 如果解析成功,返回包含完整信息的Response對(duì)象
- 如果解析失敗,創(chuàng)建一個(gè)基于HTTP狀態(tài)碼的Response對(duì)象
- 始終保留原始響應(yīng)內(nèi)容和狀態(tài)碼信息
這種設(shè)計(jì)使封裝類能夠處理各種類型的HTTP響應(yīng),同時(shí)提供一致的返回格式。
三、實(shí)際使用示例
下面是一個(gè)使用這個(gè)封裝類的完整示例:
using System; using System.Net.Http; using System.Threading.Tasks; ???????public class Program { public static async Task Main() { try { // 創(chuàng)建HttpClient實(shí)例(實(shí)際應(yīng)用中建議使用IHttpClientFactory) using var httpClient = new HttpClient(); // 創(chuàng)建請(qǐng)求實(shí)例并配置 var request = new HttpRequest(httpClient) .WithBaseAddress("https://api.example.com") .WithBearerToken("your-auth-token"); // 發(fā)送GET請(qǐng)求 var getResponse = await request.GetAsync("/api/users"); Console.WriteLine($"GET請(qǐng)求結(jié)果: {getResponse.Success}, 狀態(tài)碼: {getResponse.StatusCode}"); // 發(fā)送POST請(qǐng)求 var postData = new { Name = "John Doe", Email = "john@example.com" }; var postResponse = await request.PostAsync("/api/users", postData); Console.WriteLine($"POST請(qǐng)求結(jié)果: {postResponse.Success}, 狀態(tài)碼: {postResponse.StatusCode}"); // 發(fā)送PUT請(qǐng)求 var putData = new { Id = 1, Name = "Jane Doe" }; var putResponse = await request.PutAsync("/api/users/1", putData); Console.WriteLine($"PUT請(qǐng)求結(jié)果: {putResponse.Success}, 狀態(tài)碼: {putResponse.StatusCode}"); // 發(fā)送DELETE請(qǐng)求 var deleteResponse = await request.DeleteAsync("/api/users/1"); Console.WriteLine($"DELETE請(qǐng)求結(jié)果: {deleteResponse.Success}, 狀態(tài)碼: {deleteResponse.StatusCode}"); } catch (Exception ex) { Console.WriteLine($"發(fā)生未處理的異常: {ex.Message}"); } } }
四、HttpClient使用最佳實(shí)踐
在使用HttpClient和這個(gè)封裝類時(shí),還需要注意以下最佳實(shí)踐:
- 使用IHttpClientFactory:在ASP.NET Core應(yīng)用中,始終使用IHttpClientFactory創(chuàng)建HttpClient實(shí)例,避免直接實(shí)例化HttpClient。
- 設(shè)置合理的超時(shí)時(shí)間:默認(rèn)情況下,HttpClient的超時(shí)時(shí)間是100秒,根據(jù)實(shí)際需求調(diào)整這個(gè)值,防止長時(shí)間阻塞。
- 處理取消請(qǐng)求:考慮實(shí)現(xiàn)請(qǐng)求取消機(jī)制,通過CancellationToken參數(shù)傳遞取消令牌。
- 處理重試邏輯:對(duì)于臨時(shí)性網(wǎng)絡(luò)錯(cuò)誤,考慮實(shí)現(xiàn)重試機(jī)制??梢允褂肞olly等庫來簡化重試策略的實(shí)現(xiàn)。
- 監(jiān)控HTTP請(qǐng)求性能:記錄HTTP請(qǐng)求的執(zhí)行時(shí)間、成功率等指標(biāo),便于性能分析和問題排查。
通過這個(gè)完整的HttpRequest類封裝,我們可以更加高效、安全地處理HTTP通信,同時(shí)保持代碼的整潔和可維護(hù)性。希望這篇文章對(duì)你理解C#中的HTTP請(qǐng)求處理有所幫助。
這個(gè)實(shí)現(xiàn)提供了完整的HTTP請(qǐng)求功能,包括GET、POST、PUT、DELETE方法,以及靈活的請(qǐng)求配置和統(tǒng)一的響應(yīng)處理。
到此這篇關(guān)于C#封裝HttpClient實(shí)現(xiàn)HTTP請(qǐng)求處理的文章就介紹到這了,更多相關(guān)C#封裝HttpClient內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
winform異型不規(guī)則界面設(shè)計(jì)的實(shí)現(xiàn)方法
這篇文章主要介紹了winform異型不規(guī)則界面設(shè)計(jì)的實(shí)現(xiàn)方法,具有不錯(cuò)的實(shí)用價(jià)值,需要的朋友可以參考下2014-08-08C#爬取動(dòng)態(tài)網(wǎng)頁上信息得流程步驟
動(dòng)態(tài)內(nèi)容網(wǎng)站使用 JavaScript 腳本動(dòng)態(tài)檢索和渲染數(shù)據(jù),爬取信息時(shí)需要模擬瀏覽器行為,否則獲取到的源碼基本是空的,這篇文章主要給大家詳細(xì)介紹了C#爬取動(dòng)態(tài)網(wǎng)頁上信息得流程步驟,需要的朋友可以參考下2024-10-10超炫酷的WPF實(shí)現(xiàn)Loading控件效果
這篇文章主要介紹了超炫酷的WPF實(shí)現(xiàn)Loading控件效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2015-11-11關(guān)于C#結(jié)構(gòu)體 你需要知道的
這篇文章主要介紹了關(guān)于C#結(jié)構(gòu)體的相關(guān)知識(shí),以及使用方法,文中代碼非常詳細(xì),幫助大家更好的參考和學(xué)習(xí),感興趣的朋友可以了解下2020-06-06解析如何正確使用SqlConnection的實(shí)現(xiàn)方法
本篇文章對(duì)如何正確使用SqlConnection的實(shí)現(xiàn)方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C#實(shí)現(xiàn)任意數(shù)據(jù)類型轉(zhuǎn)成json格式輸出
C#實(shí)現(xiàn)任意數(shù)據(jù)類型轉(zhuǎn)成json格式輸出。需要的朋友可以過來參考下,希望對(duì)大家有所幫助2013-10-10Unity3D實(shí)現(xiàn)人物轉(zhuǎn)向與移動(dòng)
這篇文章主要為大家詳細(xì)介紹了Unity3D實(shí)現(xiàn)人物轉(zhuǎn)向與移動(dòng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-01-01