C#封装HttpClient实现HTTP请求处理

 更新时间:2025年06月06日 11:06:33   作者:阿蒙Armon  
HttpClient作为.NET框架中处理HTTP请求的核心组件,为我们提供了强大而灵活的API,本文将介绍一个完整的HttpRequest类封装实现,并深入探讨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和这个封装类时,还需要注意以下最佳实践:

  • 使用IHttpClientFactory:在ASP.NET Core应用中,始终使用IHttpClientFactory创建HttpClient实例,避免直接实例化HttpClient。
  • 设置合理的超时时间:默认情况下,HttpClient的超时时间是100秒,根据实际需求调整这个值,防止长时间阻塞。
  • 处理取消请求:考虑实现请求取消机制,通过CancellationToken参数传递取消令牌。
  • 处理重试逻辑:对于临时性网络错误,考虑实现重试机制。可以使用Polly等库来简化重试策略的实现。
  • 监控HTTP请求性能:记录HTTP请求的执行时间、成功率等指标,便于性能分析和问题排查。

通过这个完整的HttpRequest类封装,我们可以更加高效、安全地处理HTTP通信,同时保持代码的整洁和可维护性。希望这篇文章对你理解C#中的HTTP请求处理有所帮助。

这个实现提供了完整的HTTP请求功能,包括GET、POST、PUT、DELETE方法,以及灵活的请求配置和统一的响应处理。

到此这篇关于C#封装HttpClient实现HTTP请求处理的文章就介绍到这了,更多相关C#封装HttpClient内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C#实现将字符串转化为日期格式的方法详解

    C#实现将字符串转化为日期格式的方法详解

    这篇文章主要为大家详细介绍了C#如何使用DateTime结构的ParseExact方法和Parse方法分别将字符串转化为日期格式,有需要的小伙伴可以了解一下
    2024-01-01
  • C# 去除首尾字符或字符串的方法

    C# 去除首尾字符或字符串的方法

    C# 去除首尾字符或字符串的方法,需要的朋友可以参考一下
    2013-04-04
  • C#使用GZipStream实现文件的压缩与解压

    C#使用GZipStream实现文件的压缩与解压

    这篇文章主要为大家详细介绍了C#使用GZipStream实现文件的压缩与解压,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-10-10
  • C++实现日期类的示例详解

    C++实现日期类的示例详解

    这篇文章主要为大家详细介绍了四个C++常用的日期类的实现,文中的示例代码讲解详细,对我们学习C++有一定的帮助,需要的可以参考一下
    2023-02-02
  • C#串口关闭时主界面卡死的原因分析和解决方案

    C#串口关闭时主界面卡死的原因分析和解决方案

    最近在使用SerialPort类开发一个串口调试工具时,遇到了一个经典但令人头疼的问题:点击关闭串口按钮后,UI 界面直接卡死(假死),本文将带你从现象出发,深入.NET源码,一步步揭开这个界面卡死背后的真相,并提供一个优雅且根本性的解决方案,需要的朋友可以参考下
    2025-11-11
  • Unity游戏开发之射击小游戏的实现

    Unity游戏开发之射击小游戏的实现

    本篇文章为大家带来一个横版2D射击小游戏,游戏制作超级简单,玩法一学就会。文中的示例代码讲解详细,快跟随小编一起动手试一试
    2022-03-03
  • C# wpf实现任意控件更多拖动功能

    C# wpf实现任意控件更多拖动功能

    这篇文章主要为大家详细介绍了C# wpf如何实现任意控件(包括窗口)更多拖动功能,文中的示例代码讲解详细,有兴趣的小伙伴可以跟随小编一起学习一下
    2023-11-11
  • C#使用System.Environment获取电脑的相关属性

    C#使用System.Environment获取电脑的相关属性

    这篇文章主要为大家详细介绍了C#使用System.Environment获取电脑的相关属性,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-10-10
  • VS2015为console.readkey添加代码片段的方法

    VS2015为console.readkey添加代码片段的方法

    这篇文章主要介绍了VS2015为console.readkey添加代码片段的方法,需要的朋友可以参考下
    2016-12-12
  • C#关键字Check简单介绍

    C#关键字Check简单介绍

    这篇文章主要介绍了C#关键字Check功能描述及注意事项,checke关键字主要用于对整型类型算术运算和转换显式启用溢出检查,本文通过程序演示给大家详细介绍,需要的朋友一起看看吧
    2022-04-04

最新评论