欧美成人精品手机在线观看_69视频国产_动漫精品第一页_日韩中文字幕网 - 日本欧美一区二区

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

把 C# 里的 HttpClient 封裝起來,告別復(fù)雜的配置,讓 Restful API 調(diào)用更輕松更高效

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

前言

嗨,大家好!

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

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

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

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

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

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

封裝方法

創(chuàng)建一個 HttpUtil 類,封裝所有與 HttpClient 相關(guān)的操作方法,留意注釋

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">代理設(shè)置</param>
        /// <param name="url">請求的 URL</param>
        /// <returns>配置好的 HttpClient 實例</returns>
        public HttpClient HttpClientSettings(Dictionary<stringstring> customHeaders, ProxySetting proxySetting, string url)
        {
            // 創(chuàng)建 HttpClientHandler 并禁用自動解壓縮
            var handler = new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.None };

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

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

                // 創(chuàng)建 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);

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

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

            // 清除默認(rèn)的 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>
        /// 默認(rèn)信任證書,忽略證書警告。
        /// </summary>
        private void trustCertificate()
        {
            //默認(rèn)忽略證書
            ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
            //兼容所有ssl協(xié)議
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls | SecurityProtocolType.Ssl3;
        }

        /// <summary>
        /// 根據(jù) 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>
        /// 同步方式發(fā)送 HTTP 請求并返回響應(yīng)字符串。
        /// </summary>
        /// <param name="url">請求的 URL</param>
        /// <param name="paramlist">請求參數(shù)</param>
        /// <param name="customHeaders">自定義請求頭</param>
        /// <param name="proxySetting">代理設(shè)置</param>
        /// <param name="httpMethodType">HTTP 方法類型</param>
        /// <returns>響應(yīng)字符串</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)
                {
                    // 壓縮請求內(nèi)容
                    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!");
                }

                // 確保響應(yīng)狀態(tài)碼為成功
                response.EnsureSuccessStatusCode();

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

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

                // 讀取響應(yīng)內(nèi)容
                retString = sr.ReadToEnd();

                // 返回響應(yīng)字符串
                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>
        /// 異步發(fā)送 HTTP 請求并返回響應(yīng)字符串。
        /// </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>
    /// 構(gòu)造函數(shù),初始化 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");
        }

        // 初始化原始內(nèi)容
        _originalContent = content;

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

        // 將原始內(nèi)容的所有頭部信息復(fù)制到新的 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方法,直接設(shè)置長度為-1,返回false
    /// </summary>
    protected override bool TryComputeLength(out long length)
    {
        length = -1;
        return false;
    }

    /// <summary>
    /// 重寫基類的SerializeToStreamAsync方法,異步序列化內(nèi)容到指定的流中
    /// </summary>
    /// <param name="stream">目標(biāo)流</param>
    /// <param name="context">傳輸上下文</param>
    /// <returns>任務(wù)對象</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; }

    // 代理協(xié)議
    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; }
}

使用

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

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// 重試次數(shù)

            // 循環(huán),如果失敗,重試 5 次,成功則跳出循環(huán),規(guī)避網(wǎng)絡(luò)不穩(wěn)定帶來的問題
            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;
        }
    }
}

總結(jié)

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

你可以把這個封裝方法靈活應(yīng)用在你的項目中,相信可以極大地簡化 API 的調(diào)用流程,提高項目的可維護(hù)性和復(fù)用性

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

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


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