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

http://www.lryc.cn/news/2403561.html

相关文章:

  • 前端基础之《Vue(19)—状态管理》
  • 构建 MCP 服务器:第 4 部分 — 创建工具
  • 2.1 Windows编译环境介绍
  • 如何以 9 种方式将照片从手机传输到笔记本电脑
  • 生成JavaDoc文档
  • 八股学习-JS的闭包
  • Web后端基础(Maven基础)
  • 学习记录aigc
  • set map数据结构
  • Q: dify前端使用哪些开发框架?
  • 面试题小结(真实面试)
  • 【PmHub面试篇】PmHub中基于Redis加Lua脚本的计数器算法限流实现面试专题解析
  • 计算机网络领域所有CCF-A/B/C类期刊汇总!
  • 有意向往gis开发靠,如何规划学习?
  • 五、查询处理和查询优化
  • 缓解骨质疏松 —— 补钙和补维 D
  • 《PMBOK® 指南》第八版草案重大变革:6 大原则重构项目管理体系
  • Ctrl+R 运行xxx.exe,发现有如下问题.
  • 极智项目 | 基于PyQT+Whisper实现的语音识别软件设计
  • vue+cesium示例:地形开挖(附源码下载)
  • 升级:用vue canvas画一个能源监测设备和设备的关系监测图!
  • Elasticsearch + Milvus 构建高效知识库问答系统《一》
  • 深入理解 transforms.Normalize():PyTorch 图像预处理中的关键一步
  • leetcode 2434. 使用机器人打印字典序最小的字符串 中等
  • 爆炸仿真的学习日志
  • 【Fiddler抓取手机数据包】
  • [华为eNSP] OSPF综合实验
  • 东芝Toshiba DP-4528AG打印机信息
  • Vue3+Vite中lodash-es安装与使用指南
  • 完美搭建appium自动化环境