当前位置: 首页 > article >正文

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方法,以及灵活的请求配置和统一的响应处理。博客中详细解释了设计思路、实现要点和最佳实践。如果你需要进一步调整代码或博客内容,请随时告诉我。

相关文章:

C#封装HttpClient:HTTP请求处理最佳实践

C#封装HttpClient&#xff1a;HTTP请求处理最佳实践 在现代的.NET应用程序开发中&#xff0c;与外部服务进行HTTP通信是一项常见需求。HttpClient作为.NET框架中处理HTTP请求的核心组件&#xff0c;为我们提供了强大而灵活的API。然而&#xff0c;直接使用原生的HttpClient可能…...

前端基础之《Vue(19)—状态管理》

一、什么是状态管理 1、Vue版本问题 Vue2 Vuex3 Vue3 Vuex4 / Pinia2 在使用任何技术的时候&#xff0c;都先要去搜索一下版本&#xff0c;你的版本和脚手架环境是否兼容。 2、安装Vuex yarn add vuex3.6.2 3、状态管理 状态&#xff0c;在应用程序中表示数据&#xff0c…...

构建 MCP 服务器:第 4 部分 — 创建工具

这是我们构建 MCP 服务器的四部分教程的最后一部分。在第一部分中&#xff0c;我们使用基本资源创建了第一个 MCP 服务器。第二部分添加了资源模板并改进了代码组织。在第三部分中&#xff0c;我们添加了提示符并进一步完善了服务器结构。现在&#xff0c;我们将通过添加工具来…...

2.1 Windows编译环境介绍

一、Windows四个主要编译工具套件 MSVC&#xff1a;Windows原生编译套件&#xff0c;Microsoft Visual C&#xff0c;VS2019默认使用&#xff0c;编译生成原生Windows程序。Cygwin&#xff1a;不仅移植GCC&#xff0c;还移植了Linux命令&#xff08;如ls、mkdir、clear&#x…...

如何以 9 种方式将照片从手机传输到笔记本电脑

使用 USB 电缆可以将照片从智能手机复制到计算机。但是&#xff0c;如果没有 USB 数据线&#xff0c;如何将照片从手机无线传输到笔记本电脑呢&#xff1f;为了解决这个问题&#xff0c;我们搜索并测试了不同的应用程序&#xff0c;然后总结了本指南中分享的 9 个有效选项。您可…...

生成JavaDoc文档

生成 JavaDoc 文档 1、快速生成 文档 注解 2、常见的文档注解 3、脚本生成 doc 文档 4、IDEA工具栏生成 doc 文档 第一章 快速入门 第01节 使用插件 在插件工具当中&#xff0c;找到插件 javaDoc 使用方式&#xff0c;在代码区域&#xff0c;直接点击右键。选择 第02节 常用注…...

八股学习-JS的闭包

一.闭包的定义 闭包是指函数和其周围的词法环境的引用的组合。 简单来说&#xff0c;就是函数可以记住并访问其在定义时的作用域内的变量&#xff0c;即使该函数在其它作用域调用。 也就是说&#xff0c;闭包让你可以在一个内层函数中访问到其外层函数的作用域。 function …...

Web后端基础(Maven基础)

https://blog.csdn.net/q20202828/article/details/148459525?spm1001.2014.3001.5501 这是我总结了一下aliyun私服maven依赖配置Maven 3.9.1下载安装的操作 Maven的作用 统一项目结构 Maven 还提供了标准、统一的项目结构 。 1). 未使用Maven 由于java的开发工具呢&#x…...

学习记录aigc

1、DIT https://zhuanlan.zhihu.com/p/683612528 DiT最大的创新点是将Transformer引入到了扩散模型中&#xff0c;并完全抛弃了CNN。但是DiT并不是第一个引入Transformer的&#xff0c;例如之前的U-ViT&#xff0c;UniDiffuser等都尝试了将Transformer引入到扩散模型中。至于…...

set map数据结构

#include <set> #include <iostream> using namespace std;int main() {// 设置控制台输出编码为UTF-8system("chcp 65001");set<int> s1; // 创建一个整数集合// 插入元素s1.insert(5);s1.insert(3);s1.insert(7);s1.insert(1);s1.insert(9);//默…...

Q: dify前端使用哪些开发框架?

【回到目录】~~~~【回到问题集】 Q: dify前端使用哪些开发框架? A: 通过查看Readme.md&#xff0c;可以了解到使用以下框架 1. [Next.js] (https://nextjs.org/) React Framework 2. Node.js > v22.11.x 3. pnpm v10.x 4. Storybook UI component development 4. Je…...

面试题小结(真实面试)

面试题 1.call与apply的区别2.vue3的响应式原理3.js的垃圾回收机制4.说说原型链5.什么是防抖和节流6.说一下作用域链7.在一个页面加载数据时&#xff08;还没加载完成&#xff09;&#xff0c;切换到另一个页面&#xff0c;怎么暂停之前页面的数据加载。 浏览器自动中止机制 这…...

【PmHub面试篇】PmHub中基于Redis加Lua脚本的计数器算法限流实现面试专题解析

你好&#xff0c;欢迎来到本次关于PmHub中基于Redis加Lua脚本的计数器算法限流实现的面试系列分享。在这篇文章中&#xff0c;我们将深入探讨这一技术领域的相关面试题预测。若想对相关内容有更透彻的理解&#xff0c;强烈推荐参考之前发布的博文&#xff1a;【PmHub后端篇】Pm…...

计算机网络领域所有CCF-A/B/C类期刊汇总!

本期小编统计了【计算机网络】领域CCF推荐所有期刊的最新影响因子&#xff0c;分区、年发文量以及投稿经验&#xff0c;供大家参考&#xff01; CCF-A类 1 IEEE Journal on Selected Areas in Communications 【影响因子】13.8 【期刊分区】JCR1区&#xff0c;中科院1区TOP …...

有意向往gis开发靠,如何规划学习?

听说GIS开发工资不错、还不像互联网那么卷&#xff1f;心动了&#xff1f;但一看那些“WebGL”、“空间分析”、“OGC规范”的词儿就头大&#xff1f;别急&#xff01; 今天咱就聊聊零基础/转行选手&#xff0c;咋规划学习GIS开发这条路。不整高大上&#xff0c;就讲实在的&am…...

五、查询处理和查询优化

五、查询处理和查询优化 主要内容 查询概述查询处理过程关系操作的基本实现算法查询优化技术代数优化基于存取路径的优化基于代价估算的优化 1. 查询概述 查询是数据库管理系统中使用最频繁、最基本的操作&#xff0c;对系统性能有很大影响。 对于同一个SQL查询&#xff0c…...

缓解骨质疏松 —— 补钙和补维 D

骨质老化/疏松原理&#xff08;机制&#xff09;骨密度下降与骨小梁结构退化局部受压导致的微损伤或压力集中 诊断要点治疗策略吃什么食物能补钙呢&#xff1f;钙片吃什么食物能补维生素 D 呢&#xff1f; 骨质老化/疏松 骨质老化&#xff08;常指骨密度下降或骨质疏松&#x…...

《PMBOK® 指南》第八版草案重大变革:6 大原则重构项目管理体系

项目管理领域的权威指南迎来关键升级&#xff01;PMI 最新发布的《PMBOK 指南》第八版草案引发行业广泛关注&#xff0c;此次修订首次将项目管理原则浓缩为 6 大黄金法则&#xff0c;重构 7 大绩效域&#xff0c;并首度公开过程组与绩效域的映射关系。本文将全面解析新版核心变…...

Ctrl+R 运行xxx.exe,发现有如下问题.

CtrlR 运行xxx.exe,发现有如下问题. (1)找不到Qt5Core.all,Qt5Cored.dll,Qt5Gui.dll,Qt5Guid.dll,Qt5Widgets.all,Qt5Widgetsd.dll? (2)之后找不到libwinpthread-1.dll 从这个目录拷贝相应的库到运行xx.exe目录下 方法二:将库路径添加到系统PATH环境变量里: 在Path中添加路…...

极智项目 | 基于PyQT+Whisper实现的语音识别软件设计

这是一个基于OpenAI的Whisper模型的语音识别应用程序&#xff0c;使用PyQt5构建了简洁直观的用户界面。该应用支持多语言识别&#xff0c;特别优化了中文识别体验。 项目下载&#xff1a;链接 功能特点 简洁现代的深色主题界面支持多语言识别&#xff08;中文、英语、日语等…...

vue+cesium示例:地形开挖(附源码下载)

基于cesium和vue绘制多边形实现地形开挖效果&#xff0c;适合学习Cesium与前端框架结合开发3D可视化项目。 demo源码运行环境以及配置 运行环境&#xff1a;依赖Node安装环境&#xff0c;demo本地Node版本:推荐v18。 运行工具&#xff1a;vscode或者其他工具。 配置方式&#x…...

升级:用vue canvas画一个能源监测设备和设备的关系监测图!

用vue canvas画一个能源电表和设备的监测图-CSDN博客 上一篇文章&#xff0c;我是用后端的数据来画出监测图。这次我觉的&#xff0c;用前端来控制数据&#xff0c;更爽。 本期实现功能&#xff1a; 1&#xff0c;得到监测设备和设备的数据&#xff0c;然后进行存库 2&…...

Elasticsearch + Milvus 构建高效知识库问答系统《一》

&#x1f50d; Elasticsearch Milvus 构建高效知识库问答系统&#xff08;RAG 技术实战&#xff09; &#x1f4cc; 目录 背景介绍Elasticsearch 在知识库检索中的作用Milvus 在知识库检索中的作用混合检索&#xff1a;Elasticsearch Milvus完整代码实现部署建议与优化方向…...

深入理解 transforms.Normalize():PyTorch 图像预处理中的关键一步

深入理解 transforms.Normalize()&#xff1a;PyTorch 图像预处理中的关键一步 在使用 PyTorch 进行图像分类、目标检测等深度学习任务时&#xff0c;我们常常会在数据预处理部分看到如下代码&#xff1a; python复制编辑transform transforms.Compose([transforms.ToTensor…...

leetcode 2434. 使用机器人打印字典序最小的字符串 中等

给你一个字符串 s 和一个机器人&#xff0c;机器人当前有一个空字符串 t 。执行以下操作之一&#xff0c;直到 s 和 t 都变成空字符串&#xff1a; 删除字符串 s 的 第一个 字符&#xff0c;并将该字符给机器人。机器人把这个字符添加到 t 的尾部。删除字符串 t 的 最后一个 字…...

爆炸仿真的学习日志

今天学习了一下【Workbench LS-DYNA中炸药在空气中爆炸的案例-哔哩哔哩】 https://b23.tv/kmXlN29 一开始 如果你的 ANSYS Workbench 工具箱&#xff08;Toolbox&#xff09;里 只有 SPEOS&#xff0c;即使尝试了 右键刷新、重置视图、显示全部 等方法仍然没有其他分析系统&a…...

【Fiddler抓取手机数据包】

Fiddler抓取手机数据包的配置方法 确保电脑和手机在同一局域网 电脑和手机需连接同一Wi-Fi网络。可通过电脑命令行输入ipconfig查看电脑的本地IP地址&#xff08;IPv4地址&#xff09;&#xff0c;手机需能ping通该IP。 配置Fiddler允许远程连接 打开Fiddler&#xff0c;进入…...

[华为eNSP] OSPF综合实验

目录 配置流程 画出拓扑图、标注重要接口IP 配置客户端IP 配置服务端IP 配置服务器服务 配置路由器基本信息&#xff1a;名称和接口IP 配置路由器ospf协议 测试结果 通过配置OSPF路由协议&#xff0c;实现跨多路由器的网络互通&#xff0c;并验证终端设备的访问能力。 …...

东芝Toshiba DP-4528AG打印机信息

东芝 Toshiba DP 4528AG 是一款黑白激光数码复合机&#xff1a; 类型&#xff1a;激光数码复合机&#xff0c;涵盖复印、打印、扫描、传真功能&#xff0c;能满足办公室多样化的文档处理需求。速度类型&#xff1a;中速&#xff0c;黑白复印和打印速度可达 45 页 / 分钟&#…...

Vue3+Vite中lodash-es安装与使用指南

在 Vue 3 Vite 项目中安装和使用 lodash-es 的详细指南如下&#xff1a; 一、为什么选择 lodash-es&#xff1f; ES 模块支持&#xff1a;lodash-es 以原生 ES 模块格式发布&#xff0c;支持现代构建工具的 Tree Shaking 按需加载&#xff1a;只引入需要的函数&#xff0c;显…...