日韩欧美国产精品免费一二-日韩欧美国产精品亚洲二区-日韩欧美国产精品专区-日韩欧美国产另-日韩欧美国产免费看-日韩欧美国产免费看清风阁

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

把 C# 里的 HttpClient 封裝起來,告別復雜的配置,讓 Restful API 調用更輕松更高效

admin
2024年12月11日 9:27 本文熱度 135

前言

嗨,大家好!

在現代軟件開發中,與外部服務進行通信幾乎是不可避免的任務。無論是獲取天氣預報、同步用戶數據,還是調用第三方支付接口,HTTP 請求都是最常見的手段之一。而在 .NET 生態系統中,HttpClient 無疑是處理這類任務的最佳工具。

HttpClient 支持多種 HTTP 請求方式(如 GET、POST、PUT、DELETE),使得與 Web API 的交互變得輕而易舉。

在性能方面,它支持異步操作,這意味著你可以同時發送多個請求而不阻塞主線程,性能杠杠的!

可以說,HttpClient 是我們與 Web 服務打交道的最佳伙伴!

然而,盡管 HttpClient 功能強大,但其復雜的配置和使用方式有時會讓人望而卻步,尤其是對于初學者來說。為了簡化這一過程,我決定封裝一個通用的方法,讓團隊成員能夠更加便捷地使用 HttpClient 調用 Restful API

接下來,讓我們一起看看詳細的代碼吧!

封裝方法

創建一個 HttpUtil 類,封裝所有與 HttpClient 相關的操作方法,留意注釋

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Connect.Enums;
using System.IO;
using System.Net;
using System.IO.Compression;
using Connect.Model;
using Newtonsoft.Json;

namespace Connect.Utils
{
    public class HttpUtil
    {
        /// <summary>
        /// 配置并返回一個 HttpClient 實例
        /// </summary>
        /// <param name="customHeaders">自定義請求頭</param>
        /// <param name="proxySetting">代理設置</param>
        /// <param name="url">請求的 URL</param>
        /// <returns>配置好的 HttpClient 實例</returns>
        public HttpClient HttpClientSettings(Dictionary<stringstring> customHeaders, ProxySetting proxySetting, string url)
        {
            // 創建 HttpClientHandler 并禁用自動解壓縮
            var handler = new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.None };

            if (proxySetting != null && proxySetting.UseProxy)
            {
                // 在日志里記錄是否使用代理,方便排查問題
                Logger.CONNECT.InfoFormat("UseProxy is [{0}]", proxySetting.UseProxy);

                // 構建代理 URL
                string proxyUrl = string.Format("{0}://{1}:{2}", proxySetting.Protocol, proxySetting.Address, proxySetting.Port);
                Logger.CONNECT.InfoFormat("Proxy url is [{0}]", proxyUrl);

                // 創建 Web 代理對象
                IWebProxy webProxy = new WebProxy(proxyUrl);
                if (proxySetting.ProxyCredentials != null &&
                    !string.IsNullOrEmpty(proxySetting.ProxyCredentials.UserName) &&
                    !string.IsNullOrEmpty(proxySetting.ProxyCredentials.Password))
                {
                    Logger.CONNECT.InfoFormat("ProxyCredentials.UserName is [{0}]", proxySetting.ProxyCredentials.UserName);

                    // 設置代碼身份信息
                    webProxy.Credentials = new NetworkCredential(proxySetting.ProxyCredentials.UserName, proxySetting.ProxyCredentials.Password);
                }
                handler.Proxy = webProxy; // 設置 HttpClientHandler 的代理
                handler.UseProxy = true// 啟用代理
            }

            // 創建 HttpClient 實例
            HttpClient httpclient = new HttpClient(handler);

            // 清除默認的 Accept 頭
            httpclient.DefaultRequestHeaders.Accept.Clear();

            // 添加 Accept 頭,指定接受 JSON 格式
            httpclient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

            // 添加自定義請求頭
            if (customHeaders != null)
            {
                var headerInfo = JsonConvert.SerializeObject(customHeaders);
                Logger.CONNECT.InfoFormat("Custom header info: [{0}]", headerInfo);
                foreach (KeyValuePair<stringstring> headerItem in customHeaders)
                {
                    if (!string.IsNullOrEmpty(headerItem.Value))
                    {
                        httpclient.DefaultRequestHeaders.Add(headerItem.Key, headerItem.Value.Trim());
                    }
                }
            }

            // 獲取請求的 HTTP 模式(HTTP 或 HTTPS)
            HttpMode httpMode = getHttpModeByUrl(url);
            if (httpMode == HttpMode.HTTPS)
            {
                trustCertificate(); //如果是 HTTPS,忽略證書警告
            }

            // 返回配置好的 HttpClient 實例
            return httpclient;
        }

        /// <summary>
        /// 默認信任證書,忽略證書警告。
        /// </summary>
        private void trustCertificate()
        {
            //默認忽略證書
            ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
            //兼容所有ssl協議
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls | SecurityProtocolType.Ssl3;
        }

        /// <summary>
        /// 根據 URL 判斷 HTTP 模式(HTTP 或 HTTPS)。
        /// </summary>
        /// <param name="url">請求的 URL</param>
        /// <returns>HTTP 模式</returns>
        private HttpMode getHttpModeByUrl(string url)
        {
            HttpMode httpMode = HttpMode.HTTP;
            if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
            {
                httpMode = HttpMode.HTTPS;
            }
            return httpMode;
        }

        /// <summary>
        /// 同步方式發送 HTTP 請求并返回響應字符串。
        /// </summary>
        /// <param name="url">請求的 URL</param>
        /// <param name="paramlist">請求參數</param>
        /// <param name="customHeaders">自定義請求頭</param>
        /// <param name="proxySetting">代理設置</param>
        /// <param name="httpMethodType">HTTP 方法類型</param>
        /// <returns>響應字符串</returns>
        public string HttpclientCall(string url, HttpContent paramlist, Dictionary<stringstring> customHeaders, ProxySetting proxySetting, HttpMethodType httpMethodType)
        {
            HttpClient httpclient = null;
            Stream ResponseStream = null;
            StreamReader sr = null;
            HttpResponseMessage response = null;
            try
            {
                // 配置 HttpClient 實例
                httpclient = HttpClientSettings(customHeaders, proxySetting, url);
                CompressedContent content = null;
                if (paramlist != null)
                {
                    // 壓縮請求內容
                    content = new CompressedContent(paramlist, CompressionType.GZip);
                }
                string retString = string.Empty;

                Logger.CONNECT.InfoFormat("Call Url=[{0}], Method=[{1}]", url, httpMethodType.ToString());

                switch (httpMethodType)
                {
                    case HttpMethodType.GET:
                        response = httpclient.GetAsync(new Uri(url)).Result;
                        break;
                    case HttpMethodType.POST:
                        response = httpclient.PostAsync(new Uri(url), content).Result;
                        break;
                    case HttpMethodType.PUT:
                        response = httpclient.PutAsync(new Uri(url), content).Result;
                        break;
                    case HttpMethodType.DELETE:
                        response = httpclient.DeleteAsync(new Uri(url)).Result;
                        break;
                    default:
                        throw new NotSupportedException("This type is not supported!");
                }

                // 確保響應狀態碼為成功
                response.EnsureSuccessStatusCode();

                // 獲取響應流
                ResponseStream = response.Content.ReadAsStreamAsync().Result;

                // 創建 StreamReader 對象
                sr = new StreamReader(ResponseStream, Encoding.GetEncoding("utf-8"));

                // 讀取響應內容
                retString = sr.ReadToEnd();

                // 返回響應字符串
                return retString;
            }
            catch
            {
                throw// 重新拋出異常
            }
            finally
            {
                // 依次釋放資源
                if (response != null) response.Dispose();
                if (sr != null) sr.Close();
                if (ResponseStream != null) ResponseStream.Close();
                if (httpclient != null) httpclient.Dispose();
            }
        }

        /// <summary>
        /// 異步發送 HTTP 請求并返回響應字符串。
        /// </summary>
        public async Task<stringHttpclientCallAsync(string url, HttpContent paramlist, Dictionary<stringstring> customHeaders, ProxySetting proxySetting, HttpMethodType httpMethodType)
        {
            HttpClient httpclient = null;
            Stream ResponseStream = null;
            StreamReader sr = null;
            HttpResponseMessage response = null;
            try
            {
                httpclient = HttpClientSettings(customHeaders, proxySetting, url);
                CompressedContent content = null;
                if (paramlist != null)
                {
                    content = new CompressedContent(paramlist, CompressionType.GZip);
                }
                string retString = string.Empty;

                Logger.CONNECT.InfoFormat("Call Url=[{0}], Method=[{1}]", url, httpMethodType.ToString());

                switch (httpMethodType)
                {
                    case HttpMethodType.GET:
                        response = await httpclient.GetAsync(new Uri(url));
                        break;
                    case HttpMethodType.POST:
                        response = await httpclient.PostAsync(new Uri(url), content);
                        break;
                    case HttpMethodType.PUT:
                        response = await httpclient.PutAsync(new Uri(url), content);
                        break;
                    case HttpMethodType.DELETE:
                        response = await httpclient.DeleteAsync(new Uri(url));
                        break;
                    default:
                        throw new NotSupportedException("This type is not supported!");
                }

                response.EnsureSuccessStatusCode();

                ResponseStream = await response.Content.ReadAsStreamAsync();
                sr = new StreamReader(ResponseStream, Encoding.GetEncoding("utf-8"));
                retString = sr.ReadToEnd();

                return retString;
            }
            catch
            {
                throw;
            }
            finally
            {
                if (response != null) response.Dispose();
                if (sr != null) sr.Close();
                if (ResponseStream != null) ResponseStream.Close();
                if (httpclient != null) httpclient.Dispose();
            }
        }
    }
}

其中,CompressedContent 代碼如下:

public class CompressedContent : HttpContent
{
    private readonly HttpContent _originalContent; // 存儲原始的 HttpContent 對象
    private readonly CompressionType _compressionMethod; // 存儲壓縮方法

    /// <summary>
    /// 構造函數,初始化 CompressedContent 對象
    /// </summary>
    /// <param name="content">原始的 HttpContent 對象</param>
    /// <param name="compressionMethod">壓縮方法(GZip 或 Deflate)</param>
    public CompressedContent(HttpContent content, CompressionType compressionMethod)
    {
        if (content == null)
        {
            throw new ArgumentNullException("content");
        }

        // 初始化原始內容
        _originalContent = content;

        // 初始化壓縮方法
        _compressionMethod = compressionMethod;

        // 將原始內容的所有頭部信息復制到新的 CompressedContent 對象中
        foreach (KeyValuePair<string, IEnumerable<string>> header in _originalContent.Headers)
        {
            Headers.TryAddWithoutValidation(header.Key, header.Value);
        }

        // 添加 Content-Encoding 頭部,說明所使用的壓縮方法
        Headers.ContentEncoding.Add(_compressionMethod.ToString().ToLowerInvariant());
    }

    /// <summary>
    /// 重寫基類的TryComputeLength方法,直接設置長度為-1,返回false
    /// </summary>
    protected override bool TryComputeLength(out long length)
    {
        length = -1;
        return false;
    }

    /// <summary>
    /// 重寫基類的SerializeToStreamAsync方法,異步序列化內容到指定的流中
    /// </summary>
    /// <param name="stream">目標流</param>
    /// <param name="context">傳輸上下文</param>
    /// <returns>任務對象</returns>
    protected async override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        if (_compressionMethod == CompressionType.GZip)
        {
            using (var gzipStream = new GZipStream(stream, CompressionMode.Compress, leaveOpen: true))
            {
                await _originalContent.CopyToAsync(gzipStream);
            }
        }
        else if (_compressionMethod == CompressionType.Deflate)
        {
            using (var deflateStream = new DeflateStream(stream, CompressionMode.Compress, leaveOpen: true))
            {
                await _originalContent.CopyToAsync(deflateStream);
            }
        }
    }
}

ProxySetting 代理配置模型如下:

public class ProxySetting
{
    // 是否使用代理
    public bool UseProxy { getset; }

    // 代理協議
    public string Protocol { getset; }

    // 代理地址
    public string Address { getset; }

    // 端口
    public string Port { getset; }

    // 身份信息
    public ProxyCredentials ProxyCredentials { getset; }
}

public class ProxyCredentials
{
    public string UserName { getset; }
    public string Password { getset; }
}

使用

接下來,我們來看一個實際的具體使用例子。這是調用某個 RESTful API 上傳一些 CSV 數據:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Connect.Utils;
using Connect.Model;
using System.Net.Http;
using Newtonsoft.Json;
using System.IO;
using System.Net;
using LogLib;

namespace Connect.Restful
{
    public class RestfulApi
    {

        private Dictionary<stringstringsetCustomHeaders()
        {
            Dictionary<stringstring> customHeaders = new Dictionary<stringstring>() {
                { "job-id",  "1" },
                { "table-name""testdb" },
                { "license""li8ZeOp6XPw=" }
            };
            return customHeaders;
        }

        public APIResult UploadData(string url, string csvData, ProxySetting proxySetting)
        {
            int retry = 0// 重試次數

            // 循環,如果失敗,重試 5 次,成功則跳出循環,規避網絡不穩定帶來的問題
            while (true)
            {
                try
                {
                    StringContent body = new StringContent(csvData, Encoding.UTF8, "text/plain");
                    HttpUtil httpUtil = new HttpUtil();
                    Dictionary<stringstring> customHeaders = setCustomHeaders();
                    string responsetext = httpUtil.HttpclientCall(url, body, customHeaders, proxySetting, HttpMethodType.POST);
                    APIResult apiresult = JsonConvert.DeserializeObject<APIResult>(responsetext);
                    return apiresult;
                }
                catch (Exception ex)
                {
                    retry++;
                    System.Threading.Thread.Sleep(3000); // 每次重試間隔 5 秒
                    if (retry > 5)
                    {
                        throw;
                    }
                    else
                    {
                        Logger.CONNECT.Error("Error occured when executing UploadData method: ", ex);
                    }
                }
            }
        }

        public async Task<APIResult> UploadDataAsync(string url, string csvData, ProxySetting proxySetting)
        {
            StringContent body = new StringContent(csvData, Encoding.UTF8, "text/plain");
            HttpUtil httpUtil = new HttpUtil();
            Dictionary<stringstring> customHeaders = setCustomHeaders();
            string responsetext = await httpUtil.HttpclientCallAsync(url, body, customHeaders, proxySetting, HttpMethodType.POST);
            APIResult apiresult = JsonConvert.DeserializeObject<APIResult>(proxySetting);
            return apiresult;
        }
    }
}

總結

如果你的項目需要調用 Restful API,使用 HttpClient 絕對是一個非常明智的選擇。

你可以把這個封裝方法靈活應用在你的項目中,相信可以極大地簡化 API 的調用流程,提高項目的可維護性和復用性

需要注意的是,基于我的項目的實際情況,在這里我是采用每次調用時創建和銷毀 HttpClient 實例的方式,這種方式簡單直接,但在高頻率調用的情況下,可能會導致性能瓶頸。

因此,如果你的項目需要頻繁調用 Restful API,建議考慮使用單例模式來優化 HttpClient 的管理,更好地減少資源開銷,進一步地提升性能。


該文章在 2025/2/12 10:46:32 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved

主站蜘蛛池模板: 日韩高清在线观看不卡一区二区 | 中文字幕在线影院 | 成人免费动作大片黄在线 | 欧美性猛交xxxx乱大交蜜桃 | 大陆国语自产精品视频在 | 国产亚洲欧美另类一区 | 午夜视频在线播放 | 中文字幕一区二 | 亚洲中文字幕精品一区二区三区 | 制服丝袜另类专区制服 | 码二码三码四码 | 亚洲制服丝袜一区二区三区 | 国产乱子伦视频大全 | 国产精品拍综合在线 | 二区三区国产亚洲综合 | 成人永久免费视频网站 | 国产欧美日韩一 | a人片在线观看苍苍影院 | 男女性潮高清免费网站 | 精品国产一区二区三区不卡 | 日韩精品亚洲人旧成在线 | 亚洲成a人片在线观看www流畅 | 影院手机电影在线 | 电影免费在线观看中文字幕 | 2025国产亚洲日韩在线 | 粗又爽电影 | 又粗又硬又大又黄又爽的免 | 最新色国产精品精品视频 | 国产精品亚洲产品一区二区三区 | 日韩精品在线视频直播 | 日本视频在线观看不卡高清免费 | 国产草莓| 日本一区二区免费在线播放 | 日韩中文字幕34页视频 | 精品国产天堂 | 2025午夜小电影回乭 | 精品字幕在线亚洲 | 真人一级一级99片黄大片 | 国产精品高清尿小便嘘嘘主演 | 亚洲欧洲日产国码a | 91看片婬黄大片91挑色 |