主要功能

支持所有HTTP方法

  • GET​, POST​, PUT​, DELETE​, PATCH

  • 每个方法都有同步和异步版本

多种内容类型支持

  • JSON (application/json​)

  • Form表单 (application/x-www-form-urlencoded​)

  • 文件上传 (multipart/form-data​)

灵活的请求头管理

  • 支持每个请求自定义请求头

  • 支持全局默认请求头

  • 提供扩展方法快速添加常用请求头

完善的响应处理

  • 自动JSON反序列化

  • 详细的错误信息

  • HTTP状态码

  • 响应头信息

代码示例

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace YourNamespace.Helpers
{
    /// <summary>
    /// HTTP请求辅助类,支持同步和异步操作
    /// </summary>
    public class HttpHelper : IDisposable
    {
        private static readonly Lazy<HttpClient> _lazyHttpClient = new Lazy<HttpClient>(() => CreateHttpClient());
        private static HttpClient DefaultHttpClient => _lazyHttpClient.Value;
        
        private readonly HttpClient _httpClient;
        private bool _disposed;

        #region 构造函数

        /// <summary>
        /// 使用默认HttpClient实例
        /// </summary>
        public HttpHelper()
        {
            _httpClient = DefaultHttpClient;
        }

        /// <summary>
        /// 使用自定义HttpClient实例
        /// </summary>
        public HttpHelper(HttpClient httpClient)
        {
            _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
        }

        /// <summary>
        /// 使用自定义配置创建HttpClient
        /// </summary>
        public HttpHelper(HttpClientConfig config)
        {
            _httpClient = CreateHttpClient(config);
        }

        #endregion

        #region 静态工厂方法

        private static HttpClient CreateHttpClient(HttpClientConfig config = null)
        {
            // 忽略SSL证书验证(生产环境慎用)
            // 使用全局方式,兼容 .NET Framework 4.5+
            if (config?.IgnoreSslErrors ?? false)
            {
                ServicePointManager.ServerCertificateValidationCallback = 
                    new RemoteCertificateValidationCallback(
                        (sender, certificate, chain, sslPolicyErrors) => true
                    );
                
                // 同时设置安全协议(支持 TLS 1.2)
                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 
                    | SecurityProtocolType.Tls11 
                    | SecurityProtocolType.Tls;
            }

            var handler = new HttpClientHandler
            {
                AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
                UseCookies = config?.UseCookies ?? true,
                AllowAutoRedirect = config?.AllowAutoRedirect ?? true,
                MaxAutomaticRedirections = config?.MaxRedirections ?? 50
            };

            // .NET Framework 4.7.1+ 可以使用此方式
            #if NET471_OR_GREATER || NETCOREAPP || NETSTANDARD2_1_OR_GREATER
            if (config?.IgnoreSslErrors ?? false)
            {
                handler.ServerCertificateCustomValidationCallback = 
                    (message, cert, chain, errors) => true;
            }
            #endif

            var client = new HttpClient(handler)
            {
                Timeout = TimeSpan.FromSeconds(config?.TimeoutSeconds ?? 100)
            };

            // 设置默认请求头
            if (config?.DefaultHeaders != null)
            {
                foreach (var header in config.DefaultHeaders)
                {
                    client.DefaultRequestHeaders.TryAddWithoutValidation(header.Key, header.Value);
                }
            }

            return client;
        }

        #endregion

        #region GET 请求

        /// <summary>
        /// 异步GET请求
        /// </summary>
        public async Task<HttpResponse<string>> GetAsync(
            string url,
            Dictionary<string, string> headers = null,
            CancellationToken cancellationToken = default)
        {
            return await SendRequestAsync(HttpMethod.Get, url, null, headers, cancellationToken);
        }

        /// <summary>
        /// 异步GET请求并反序列化为指定类型
        /// </summary>
        public async Task<HttpResponse<T>> GetAsync<T>(
            string url,
            Dictionary<string, string> headers = null,
            CancellationToken cancellationToken = default)
        {
            var response = await GetAsync(url, headers, cancellationToken);
            return ConvertResponse<T>(response);
        }

        /// <summary>
        /// 同步GET请求
        /// </summary>
        public HttpResponse<string> Get(string url, Dictionary<string, string> headers = null)
        {
            return GetAsync(url, headers).GetAwaiter().GetResult();
        }

        /// <summary>
        /// 同步GET请求并反序列化为指定类型
        /// </summary>
        public HttpResponse<T> Get<T>(string url, Dictionary<string, string> headers = null)
        {
            return GetAsync<T>(url, headers).GetAwaiter().GetResult();
        }

        #endregion

        #region POST 请求

        /// <summary>
        /// 异步POST请求(JSON)
        /// </summary>
        public async Task<HttpResponse<string>> PostJsonAsync(
            string url,
            object data,
            Dictionary<string, string> headers = null,
            CancellationToken cancellationToken = default)
        {
            var content = CreateJsonContent(data);
            return await SendRequestAsync(HttpMethod.Post, url, content, headers, cancellationToken);
        }

        /// <summary>
        /// 异步POST请求(JSON)并反序列化
        /// </summary>
        public async Task<HttpResponse<T>> PostJsonAsync<T>(
            string url,
            object data,
            Dictionary<string, string> headers = null,
            CancellationToken cancellationToken = default)
        {
            var response = await PostJsonAsync(url, data, headers, cancellationToken);
            return ConvertResponse<T>(response);
        }

        /// <summary>
        /// 同步POST请求(JSON)
        /// </summary>
        public HttpResponse<string> PostJson(string url, object data, Dictionary<string, string> headers = null)
        {
            return PostJsonAsync(url, data, headers).GetAwaiter().GetResult();
        }

        /// <summary>
        /// 同步POST请求(JSON)并反序列化
        /// </summary>
        public HttpResponse<T> PostJson<T>(string url, object data, Dictionary<string, string> headers = null)
        {
            return PostJsonAsync<T>(url, data, headers).GetAwaiter().GetResult();
        }

        /// <summary>
        /// 异步POST请求(Form表单)
        /// </summary>
        public async Task<HttpResponse<string>> PostFormAsync(
            string url,
            Dictionary<string, string> formData,
            Dictionary<string, string> headers = null,
            CancellationToken cancellationToken = default)
        {
            var content = new FormUrlEncodedContent(formData);
            return await SendRequestAsync(HttpMethod.Post, url, content, headers, cancellationToken);
        }

        /// <summary>
        /// 同步POST请求(Form表单)
        /// </summary>
        public HttpResponse<string> PostForm(
            string url,
            Dictionary<string, string> formData,
            Dictionary<string, string> headers = null)
        {
            return PostFormAsync(url, formData, headers).GetAwaiter().GetResult();
        }

        /// <summary>
        /// 异步POST请求(Multipart文件上传)
        /// </summary>
        public async Task<HttpResponse<string>> PostFileAsync(
            string url,
            byte[] fileContent,
            string fileName,
            Dictionary<string, string> formData = null,
            Dictionary<string, string> headers = null,
            CancellationToken cancellationToken = default)
        {
            var content = new MultipartFormDataContent();
            
            // 添加文件
            var fileContent2 = new ByteArrayContent(fileContent);
            fileContent2.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
            content.Add(fileContent2, "file", fileName);
            
            // 添加其他表单数据
            if (formData != null)
            {
                foreach (var item in formData)
                {
                    content.Add(new StringContent(item.Value), item.Key);
                }
            }

            return await SendRequestAsync(HttpMethod.Post, url, content, headers, cancellationToken);
        }

        #endregion

        #region PUT 请求

        /// <summary>
        /// 异步PUT请求(JSON)
        /// </summary>
        public async Task<HttpResponse<string>> PutJsonAsync(
            string url,
            object data,
            Dictionary<string, string> headers = null,
            CancellationToken cancellationToken = default)
        {
            var content = CreateJsonContent(data);
            return await SendRequestAsync(HttpMethod.Put, url, content, headers, cancellationToken);
        }

        /// <summary>
        /// 异步PUT请求(JSON)并反序列化
        /// </summary>
        public async Task<HttpResponse<T>> PutJsonAsync<T>(
            string url,
            object data,
            Dictionary<string, string> headers = null,
            CancellationToken cancellationToken = default)
        {
            var response = await PutJsonAsync(url, data, headers, cancellationToken);
            return ConvertResponse<T>(response);
        }

        /// <summary>
        /// 同步PUT请求(JSON)
        /// </summary>
        public HttpResponse<string> PutJson(string url, object data, Dictionary<string, string> headers = null)
        {
            return PutJsonAsync(url, data, headers).GetAwaiter().GetResult();
        }

        /// <summary>
        /// 同步PUT请求(JSON)并反序列化
        /// </summary>
        public HttpResponse<T> PutJson<T>(string url, object data, Dictionary<string, string> headers = null)
        {
            return PutJsonAsync<T>(url, data, headers).GetAwaiter().GetResult();
        }

        #endregion

        #region DELETE 请求

        /// <summary>
        /// 异步DELETE请求
        /// </summary>
        public async Task<HttpResponse<string>> DeleteAsync(
            string url,
            Dictionary<string, string> headers = null,
            CancellationToken cancellationToken = default)
        {
            return await SendRequestAsync(HttpMethod.Delete, url, null, headers, cancellationToken);
        }

        /// <summary>
        /// 异步DELETE请求并反序列化
        /// </summary>
        public async Task<HttpResponse<T>> DeleteAsync<T>(
            string url,
            Dictionary<string, string> headers = null,
            CancellationToken cancellationToken = default)
        {
            var response = await DeleteAsync(url, headers, cancellationToken);
            return ConvertResponse<T>(response);
        }

        /// <summary>
        /// 同步DELETE请求
        /// </summary>
        public HttpResponse<string> Delete(string url, Dictionary<string, string> headers = null)
        {
            return DeleteAsync(url, headers).GetAwaiter().GetResult();
        }

        /// <summary>
        /// 同步DELETE请求并反序列化
        /// </summary>
        public HttpResponse<T> Delete<T>(string url, Dictionary<string, string> headers = null)
        {
            return DeleteAsync<T>(url, headers).GetAwaiter().GetResult();
        }

        #endregion

        #region PATCH 请求

        /// <summary>
        /// 异步PATCH请求(JSON)
        /// </summary>
        public async Task<HttpResponse<string>> PatchJsonAsync(
            string url,
            object data,
            Dictionary<string, string> headers = null,
            CancellationToken cancellationToken = default)
        {
            var content = CreateJsonContent(data);
            return await SendRequestAsync(new HttpMethod("PATCH"), url, content, headers, cancellationToken);
        }

        /// <summary>
        /// 异步PATCH请求(JSON)并反序列化
        /// </summary>
        public async Task<HttpResponse<T>> PatchJsonAsync<T>(
            string url,
            object data,
            Dictionary<string, string> headers = null,
            CancellationToken cancellationToken = default)
        {
            var response = await PatchJsonAsync(url, data, headers, cancellationToken);
            return ConvertResponse<T>(response);
        }

        /// <summary>
        /// 同步PATCH请求(JSON)
        /// </summary>
        public HttpResponse<string> PatchJson(string url, object data, Dictionary<string, string> headers = null)
        {
            return PatchJsonAsync(url, data, headers).GetAwaiter().GetResult();
        }

        #endregion

        #region 核心请求方法

        /// <summary>
        /// 发送HTTP请求的核心方法
        /// </summary>
        private async Task<HttpResponse<string>> SendRequestAsync(
            HttpMethod method,
            string url,
            HttpContent content,
            Dictionary<string, string> headers,
            CancellationToken cancellationToken)
        {
            var response = new HttpResponse<string>();
            
            try
            {
                using (var request = new HttpRequestMessage(method, url))
                {
                    // 添加请求头
                    if (headers != null)
                    {
                        foreach (var header in headers)
                        {
                            request.Headers.TryAddWithoutValidation(header.Key, header.Value);
                        }
                    }

                    // 添加请求内容
                    if (content != null)
                    {
                        request.Content = content;
                    }

                    // 发送请求
                    using (var httpResponse = await _httpClient.SendAsync(request, cancellationToken))
                    {
                        response.StatusCode = (int)httpResponse.StatusCode;
                        response.IsSuccess = httpResponse.IsSuccessStatusCode;
                        response.Content = await httpResponse.Content.ReadAsStringAsync();
                        response.Headers = new Dictionary<string, string>();

                        // 获取响应头
                        foreach (var header in httpResponse.Headers)
                        {
                            response.Headers[header.Key] = string.Join(",", header.Value);
                        }

                        if (!httpResponse.IsSuccessStatusCode)
                        {
                            response.ErrorMessage = $"HTTP请求失败: {httpResponse.StatusCode} - {httpResponse.ReasonPhrase}";
                        }
                    }
                }
            }
            catch (TaskCanceledException ex)
            {
                response.IsSuccess = false;
                response.ErrorMessage = "请求超时";
                response.Exception = ex;
            }
            catch (HttpRequestException ex)
            {
                response.IsSuccess = false;
                response.ErrorMessage = $"HTTP请求异常: {ex.Message}";
                response.Exception = ex;
            }
            catch (Exception ex)
            {
                response.IsSuccess = false;
                response.ErrorMessage = $"请求发生异常: {ex.Message}";
                response.Exception = ex;
            }

            return response;
        }

        #endregion

        #region 辅助方法

        /// <summary>
        /// 创建JSON内容
        /// </summary>
        private StringContent CreateJsonContent(object data)
        {
            var json = data is string str ? str : JsonConvert.SerializeObject(data);
            return new StringContent(json, Encoding.UTF8, "application/json");
        }

        /// <summary>
        /// 转换响应类型
        /// </summary>
        private HttpResponse<T> ConvertResponse<T>(HttpResponse<string> response)
        {
            var result = new HttpResponse<T>
            {
                StatusCode = response.StatusCode,
                IsSuccess = response.IsSuccess,
                ErrorMessage = response.ErrorMessage,
                Exception = response.Exception,
                Headers = response.Headers
            };

            if (response.IsSuccess && !string.IsNullOrWhiteSpace(response.Content))
            {
                try
                {
                    result.Data = JsonConvert.DeserializeObject<T>(response.Content);
                }
                catch (JsonException ex)
                {
                    result.IsSuccess = false;
                    result.ErrorMessage = $"JSON反序列化失败: {ex.Message}";
                    result.Exception = ex;
                }
            }

            return result;
        }

        #endregion

        #region IDisposable

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (_disposed) return;

            if (disposing)
            {
                // 只有非默认实例才释放
                if (_httpClient != DefaultHttpClient)
                {
                    _httpClient?.Dispose();
                }
            }

            _disposed = true;
        }

        #endregion
    }

    #region 配置类和响应类

    /// <summary>
    /// HttpClient配置
    /// </summary>
    public class HttpClientConfig
    {
        /// <summary>
        /// 超时时间(秒)
        /// </summary>
        public int TimeoutSeconds { get; set; } = 100;

        /// <summary>
        /// 是否使用Cookie
        /// </summary>
        public bool UseCookies { get; set; } = true;

        /// <summary>
        /// 是否允许自动重定向
        /// </summary>
        public bool AllowAutoRedirect { get; set; } = true;

        /// <summary>
        /// 最大重定向次数
        /// </summary>
        public int MaxRedirections { get; set; } = 50;

        /// <summary>
        /// 是否忽略SSL证书错误(生产环境不建议使用)
        /// </summary>
        public bool IgnoreSslErrors { get; set; } = false;

        /// <summary>
        /// 默认请求头
        /// </summary>
        public Dictionary<string, string> DefaultHeaders { get; set; }
    }

    /// <summary>
    /// HTTP响应结果
    /// </summary>
    public class HttpResponse<T>
    {
        /// <summary>
        /// 是否成功
        /// </summary>
        public bool IsSuccess { get; set; }

        /// <summary>
        /// HTTP状态码
        /// </summary>
        public int StatusCode { get; set; }

        /// <summary>
        /// 响应内容(泛型)
        /// </summary>
        public T Data { get; set; }

        /// <summary>
        /// 原始响应内容(仅string类型)
        /// </summary>
        public string Content { get; set; }

        /// <summary>
        /// 错误信息
        /// </summary>
        public string ErrorMessage { get; set; }

        /// <summary>
        /// 异常信息
        /// </summary>
        public Exception Exception { get; set; }

        /// <summary>
        /// 响应头
        /// </summary>
        public Dictionary<string, string> Headers { get; set; }
    }

    #endregion

    #region 扩展方法

    /// <summary>
    /// HttpHelper扩展方法
    /// </summary>
    public static class HttpHelperExtensions
    {
        /// <summary>
        /// 添加Bearer Token
        /// </summary>
        public static Dictionary<string, string> WithBearerToken(this Dictionary<string, string> headers, string token)
        {
            headers = headers ?? new Dictionary<string, string>();
            headers["Authorization"] = $"Bearer {token}";
            return headers;
        }

        /// <summary>
        /// 添加自定义请求头
        /// </summary>
        public static Dictionary<string, string> WithHeader(this Dictionary<string, string> headers, string key, string value)
        {
            headers = headers ?? new Dictionary<string, string>();
            headers[key] = value;
            return headers;
        }

        /// <summary>
        /// 创建请求头
        /// </summary>
        public static Dictionary<string, string> CreateHeaders(string key, string value)
        {
            return new Dictionary<string, string> { { key, value } };
        }
    }

    #endregion
}

使用示例

// ============= 基本使用 =============

// 1. GET请求
var httpHelper = new HttpHelper();

// 异步方式
var response = await httpHelper.GetAsync<User>("https://api.example.com/users/1");
if (response.IsSuccess)
{
    var user = response.Data;
}

// 同步方式
var response2 = httpHelper.Get<User>("https://api.example.com/users/1");


// 2. POST请求(JSON)
var loginForm = new { username = "admin", password = "123456" };

// 异步
var result = await httpHelper.PostJsonAsync<LoginResponse>(
    "https://api.example.com/login", 
    loginForm
);

// 同步
var result2 = httpHelper.PostJson<LoginResponse>(
    "https://api.example.com/login", 
    loginForm
);


// ============= 带请求头的使用 =============

// 方式1:直接传入字典
var headers = new Dictionary<string, string>
{
    { "Authorization", "Bearer your_token_here" },
    { "Custom-Header", "value" }
};

var response3 = await httpHelper.GetAsync<User>(
    "https://api.example.com/users/1", 
    headers
);


// 方式2:使用扩展方法(推荐)
var response4 = await httpHelper.GetAsync<User>(
    "https://api.example.com/users/1",
    new Dictionary<string, string>()
        .WithBearerToken("your_token_here")
        .WithHeader("Custom-Header", "value")
);


// 方式3:创建请求头
var response5 = await httpHelper.GetAsync<User>(
    "https://api.example.com/users/1",
    HttpHelperExtensions.CreateHeaders("Authorization", "Bearer token")
);


// ============= 自定义配置 =============

var config = new HttpClientConfig
{
    TimeoutSeconds = 30,
    IgnoreSslErrors = true, // 仅用于测试环境
    DefaultHeaders = new Dictionary<string, string>
    {
        { "User-Agent", "MyApp/1.0" }
    }
};

var customHelper = new HttpHelper(config);


// ============= 表单提交 =============

var formData = new Dictionary<string, string>
{
    { "username", "admin" },
    { "password", "123456" }
};

var formResult = await httpHelper.PostFormAsync(
    "https://api.example.com/login",
    formData
);


// ============= 文件上传 =============

byte[] fileBytes = File.ReadAllBytes("document.pdf");
var uploadResult = await httpHelper.PostFileAsync(
    "https://api.example.com/upload",
    fileBytes,
    "document.pdf",
    formData: new Dictionary<string, string> { { "description", "My file" } }
);


// ============= 完整的错误处理示例 =============

var response6 = await httpHelper.GetAsync<User>(
    "https://api.example.com/users/1",
    headers
);

if (response6.IsSuccess)
{
    Console.WriteLine($"用户名: {response6.Data.Name}");
}
else
{
    Console.WriteLine($"请求失败: {response6.ErrorMessage}");
    Console.WriteLine($"状态码: {response6.StatusCode}");
    
    if (response6.Exception != null)
    {
        Console.WriteLine($"异常: {response6.Exception.Message}");
    }
}