C#封裝HttpClient:HTTP請求處理最佳實踐

C#封裝HttpClient:HTTP請求處理最佳實踐

在現代的.NET應用程序開發中,與外部服務進行HTTP通信是一項常見需求。HttpClient作為.NET框架中處理HTTP請求的核心組件,為我們提供了強大而靈活的API。然而,直接使用原生的HttpClient可能會導致代碼重復、錯誤處理不完善等問題。為了提高代碼的可維護性和可測試性,我們通常會對HttpClient進行封裝。本文將介紹一個完整的HttpRequest類封裝實現,并深入探討HTTP請求處理的最佳實踐。

一、完整的HttpRequest類實現

首先,讓我們來看一下完整的HttpRequest類實現代碼:

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響應var responseObject = JsonSerializer.Deserialize<Response>(responseContent, JsonConverterExtensions.SerializerSettings);if (responseObject != null){responseObject.StatusCode = response.StatusCode;return responseObject;}}catch (JsonException){// 如果JSON解析失敗,創建一個基于HTTP狀態碼的響應}// 對于非JSON響應或解析失敗的情況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 = $"處理請求時發生意外錯誤: {ex.Message}",StatusCode = HttpStatusCode.InternalServerError,Data = ex};}
}

二、設計思路與實現要點

1. 依賴注入與生命周期管理

這個封裝類采用了依賴注入模式,通過構造函數接收一個HttpClient實例。這樣做有幾個重要好處:

  • 遵循單一職責原則,HttpRequest類專注于HTTP請求處理
  • 便于單元測試,可以輕松注入模擬的HttpClient
  • 利用.NET的IHttpClientFactory進行正確的HttpClient生命周期管理,避免資源泄漏

同時,類實現了IDisposable接口,確保在不再需要時正確釋放HttpClient資源。

2. 流暢接口設計

為了提供更友好的API體驗,封裝類實現了流暢接口模式:

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" });

這種鏈式調用方式使代碼更加簡潔易讀,同時保持了良好的可擴展性。

3. 統一的錯誤處理

在每個HTTP方法中,我們都實現了統一的異常處理機制:

  • 捕獲HttpRequestException處理HTTP特定錯誤
  • 捕獲其他異常處理意外錯誤
  • 將所有錯誤轉換為統一的Response對象
  • 保留原始異常信息以便調試

這種統一的錯誤處理方式使上層調用代碼更加簡潔,無需重復處理各種異常情況。

4. 靈活的響應處理

ProcessResponseAsync方法負責處理HTTP響應,它嘗試將響應內容解析為JSON格式的Response對象:

  • 如果解析成功,返回包含完整信息的Response對象
  • 如果解析失敗,創建一個基于HTTP狀態碼的Response對象
  • 始終保留原始響應內容和狀態碼信息

這種設計使封裝類能夠處理各種類型的HTTP響應,同時提供一致的返回格式。

三、實際使用示例

下面是一個使用這個封裝類的完整示例:

using System;
using System.Net.Http;
using System.Threading.Tasks;public class Program
{public static async Task Main(){try{// 創建HttpClient實例(實際應用中建議使用IHttpClientFactory)using var httpClient = new HttpClient();// 創建請求實例并配置var request = new HttpRequest(httpClient).WithBaseAddress("https://api.example.com").WithBearerToken("your-auth-token");// 發送GET請求var getResponse = await request.GetAsync("/api/users");Console.WriteLine($"GET請求結果: {getResponse.Success}, 狀態碼: {getResponse.StatusCode}");// 發送POST請求var postData = new { Name = "John Doe", Email = "john@example.com" };var postResponse = await request.PostAsync("/api/users", postData);Console.WriteLine($"POST請求結果: {postResponse.Success}, 狀態碼: {postResponse.StatusCode}");// 發送PUT請求var putData = new { Id = 1, Name = "Jane Doe" };var putResponse = await request.PutAsync("/api/users/1", putData);Console.WriteLine($"PUT請求結果: {putResponse.Success}, 狀態碼: {putResponse.StatusCode}");// 發送DELETE請求var deleteResponse = await request.DeleteAsync("/api/users/1");Console.WriteLine($"DELETE請求結果: {deleteResponse.Success}, 狀態碼: {deleteResponse.StatusCode}");}catch (Exception ex){Console.WriteLine($"發生未處理的異常: {ex.Message}");}}
}

四、HttpClient使用最佳實踐

在使用HttpClient和這個封裝類時,還需要注意以下最佳實踐:

    1. 使用IHttpClientFactory:在ASP.NET Core應用中,始終使用IHttpClientFactory創建HttpClient實例,避免直接實例化HttpClient
    1. 設置合理的超時時間:默認情況下,HttpClient的超時時間是100秒,根據實際需求調整這個值,防止長時間阻塞。
    1. 處理取消請求:考慮實現請求取消機制,通過CancellationToken參數傳遞取消令牌。
    1. 處理重試邏輯:對于臨時性網絡錯誤,考慮實現重試機制。可以使用Polly等庫來簡化重試策略的實現。
    1. 監控HTTP請求性能:記錄HTTP請求的執行時間、成功率等指標,便于性能分析和問題排查。

通過這個完整的HttpRequest類封裝,我們可以更加高效、安全地處理HTTP通信,同時保持代碼的整潔和可維護性。希望這篇文章對你理解C#中的HTTP請求處理有所幫助。

這個實現提供了完整的HTTP請求功能,包括GET、POST、PUT、DELETE方法,以及靈活的請求配置和統一的響應處理。博客中詳細解釋了設計思路、實現要點和最佳實踐。如果你需要進一步調整代碼或博客內容,請隨時告訴我。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/86355.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/86355.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/86355.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Redis/2】核心特性、應用場景與安裝配置

文章目錄 一、初識 Redis1.1 Redis 概述1. Redis 簡介2. Redis 的發展歷程 1.2 Redis 核心特性1. 高性能2. 豐富的數據類型3. 持久化4. 原子操作5. 主從復制6. 高可用性與分布式7. 內存存儲與低延遲8. 靈活的過期策略9. 事務支持10. 簡單的 API總結 1.3 Redis 應用場景Redis 適…

AI大模型在測試領域應用案例拆解:AI賦能的軟件測試效能躍遷的四大核心引擎(順豐科技)

導語 5月份QECon深圳大會已經結束&#xff0c;繼續更新一下案例拆解&#xff0c;本期是來自順豐科技。 文末附完整版材料獲取方式。 首先來看一下這個案例的核心內容&#xff0c;涵蓋了測四用例設計、CI/CD輔助、測試執行、監控預警四大方面&#xff0c;也是算大家比較熟悉的…

【HTML】HTML 與 CSS 基礎教程

作為 Java 工程師&#xff0c;掌握 HTML 和 CSS 也是需要的&#xff0c;它能讓你高效與前端團隊協作、調試頁面元素&#xff0c;甚至獨立完成簡單頁面開發。本文將用最簡潔的方式帶你掌握核心概念。 一、HTML&#xff0c;網頁骨架搭建 核心概念&#xff1a;HTML通過標簽定義內…

Redis 集群批量刪除key報錯 CROSSSLOT Keys in request don‘t hash to the same slot

Redis 集群報錯 CROSSSLOT Keys in request dont hash to the same slot 的原因及解決方案 1. 錯誤原因 在 Redis 集群模式下&#xff0c;數據根據 哈希槽&#xff08;Slot&#xff09; 分散存儲在不同的節點上&#xff08;默認 16384 個槽&#xff09;。當執行涉及多個 key …

.Net Framework 4/C# LINQ*

一、什么是 LINQ LINQ 是一種在 C# 等編程語言中集成的查詢功能&#xff0c;它允許開發者使用編程語言本身的語法進行數據查詢&#xff0c;而不是嵌入式的字符串 SQL 語句。LINQ 查詢可以應用于對象、XML 和數據庫等多種數據源。 二、LINQ 查詢的基本構成 LINQ 查詢通常包含以…

【docker】容器技術如何改變軟件開發與部署格局

在當今數字化時代&#xff0c;軟件開發與部署的效率和靈活性至關重要。就像古人云&#xff1a;“工欲善其事&#xff0c;必先利其器。”Docker 作為一款強大的容器技術&#xff0c;正如同軟件開發領域的一把利器&#xff0c;極大地改變了應用的開發、交付和運行方式。本文將深入…

MySQL的優化部分介紹

1、定期維護表&#xff1a; ANALYZE TABLE t_order_package; OPTIMIZE TABLE t_order_package; -- 每月在低峰期執行 2、數據歸檔&#xff08;如果create_time較舊&#xff09;&#xff1a; -- 歸檔舊數據到歷史表 INSERT INTO t_order_package_archive SELECT * FROM t_or…

Go基本語法——go語言中的四種變量定義方法

前言 在go語言中&#xff0c;定義一個變量有四種方式&#xff0c;本文單從語法的層面來介紹這幾種方式 單變量定義方法 1.var 變量名 類型&#xff0c;不進行初始化 例如&#xff0c;定義一個變量a后為其賦值&#xff0c;并且打印其值&#xff0c;運行結果如下 //1.不進行…

C++ 對 C 的兼容性

C 對 C 語言的兼容性是有限且有條件的&#xff0c;并非完全無縫兼容。這種兼容性主要體現在語法、標準庫和運行時特性上&#xff0c;但存在一些關鍵差異和不兼容點。以下是詳細分析&#xff1a; 一、C 對 C 的兼容性表現 1. 語法兼容&#xff1a;大部分 C 代碼可直接編譯 基…

ES6 核心語法手冊

ES6 核心語法手冊 一、變量聲明 關鍵字作用域是否可重定義是否可修改特性let塊級作用域??替代 var 的首選const塊級作用域??聲明常量&#xff08;對象屬性可修改&#xff09; // 示例 let name "Alice"; name "Bob"; // ?const PI 3.14; // PI …

react菜單,動態綁定點擊事件,菜單分離出去單獨的js文件,Ant框架

1、菜單文件treeTop.js // 頂部菜單 import { AppstoreOutlined, SettingOutlined } from ant-design/icons; // 定義菜單項數據 const treeTop [{label: Docker管理,key: 1,icon: <AppstoreOutlined />,url:"/docker/index"},{label: 權限管理,key: 2,icon:…

gRPC協議

目錄 1. gRPC協議介紹及構成 協議分層 協議關鍵字段 2. 示例&#xff1a;Greeter 服務 步驟1&#xff1a;定義 .proto 文件 步驟2&#xff1a;生成代碼 3. Java代碼示例 依賴配置&#xff08;Maven pom.xml&#xff09; 服務端實現 客戶端實現 運行流程 關鍵機制 …

深度學習 w b

在深度學習中&#xff0c;權重 w 和 偏置 b 是神經網絡的核心參數&#xff0c;它們的形態&#xff08;shape&#xff09;取決于網絡結構和數據維度。以下是關于 w 和 b 的詳細解析&#xff1a; 1. 數學表示與物理意義 權重 w&#xff1a; 連接神經元之間的強度&#xff0c;決定…

el-table 樹形數據,子行數據可以異步加載

1、 <el-tableborder:header-cell-style"tableStyle?.headerCellStyle"ref"tableRef":data"tableData"row-key"id":default-expand-all"false" // 默認不展開所有樹形節點:tree-props"{ children: children, hasC…

Vue中渲染函數的使用

Vue中渲染函數的使用 1. render函數2. h()的使用3. render函數和h函數的區分 vue中的渲染函數&#xff1a; 1.template2.render函數3.jsx -> js extension(jsx也是編譯成render函數&#xff0c;可編程能力更強) 1. render函數 1.1. 認識h函數 1.1.1. Vue推薦在絕大多數情況…

【氮化鎵】GaN HMETs器件物理失效分析進展

2021 年 5 月,南京大學的蔡曉龍等人在《Journal of Semiconductors》期刊發表了題為《Recent progress of physical failure analysis of GaN HEMTs》的文章,基于多種物理表征技術及大量研究成果,對 GaN HEMTs 的常見失效機制進行了系統分析。文中先介紹失效分析流程,包括使…

每日Prompt:治愈動漫插畫

提示詞 現代都市治愈動漫插畫風格&#xff0c;現代女子&#xff0c;漂亮&#xff0c;長直發&#xff0c;20歲&#xff0c;豆沙唇&#xff0c;白皙&#xff0c;氣質&#xff0c;清純現代都市背景下&#xff0c;夕陽西下&#xff0c;一位穿著白色露臍短袖&#xff0c;粉色工裝褲…

2025年牛客網秋招/社招高質量 Java 面試八股文整理

Java 面試 不論是校招還是社招都避免不了各種面試。筆試&#xff0c;如何去準備這些東西就顯得格外重要。不論是筆試還是面試都是有章可循的。關鍵在于理解企業的需求&#xff0c;明確自己的定位&#xff0c;以及掌握一定的應試技巧。 筆試部分&#xff0c;通常是對基礎知識、…

在UI界面內修改了對象名,在#include “ui_mainwindow.h“沒更新

?原因?&#xff1a;未重新編譯UI文件?? Qt的UI文件&#xff08;.ui&#xff09;需要通過??uic工具&#xff08;Qt的UI編譯器&#xff09;??生成對應的ui_*.h頭文件。如果你在Qt Designer中修改了對象名&#xff0c;但沒有??重新構建&#xff08;Rebuild&#xff09;…

前端獲取接口數據流程

一、Free-Table組件分析 <free-table v-show"showTable" v-model:page"params.pageNum" 雙向綁定當前頁大小&#xff0c;支持動態更新 v-model:limit"params.pageSize" 雙向綁定每頁大小&#xff0c;支持動態更新 v-loading&…