首页>动态>正文
C# abp框架Http辅助类 今日讯
2023-04-25 18:36:49    来源:博客园


(资料图片仅供参考)

一、定义接口

为什么要定义接口而不直接使用静态类,因为接口可以注入缓存对象,这样就能从缓存中读取指定的请求头

using System;using System.Collections.Generic;using System.Net.Http;using System.Text;using System.Threading.Tasks;using Volo.Abp.Application.Services;namespace Test.Common{    ///     /// HTTP请求接口    ///     public interface IHttpClientUtils: IApplicationService    {        ///         /// 发送HTTP请求,注意Get请求第4个参数才是缓存key,因此第3个参数请设置为null        ///         /// HttpMethod.Get、HttpMethod.Post、HttpMethod.Put、HttpMethod.Delete        /// 完整地址        /// 请求体对象        /// 鉴权请求头缓存key        /// 请求头        ///         Task SendAsync(HttpMethod method, string url, object model = null, string cacheKey = "", Dictionary headers = null);        ///         /// 发送HTTP表单请求        ///         /// HttpMethod.Post、HttpMethod.Put        /// 完整地址        /// 请求体对象        /// 鉴权请求头缓存key        /// 请求头        ///         Task SendFormAsync(HttpMethod method, string url, object model, string cacheKey = "", Dictionary headers = null);    }}
二、实现接口

1、支持https

2、支持从缓存读取鉴权请求头

3、支持对象携带文件上传

using Microsoft.Extensions.Logging;using Newtonsoft.Json;using RestSharp;using System;using System.Collections.Generic;using System.IO;using System.Linq;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.Tasks;using Volo.Abp.Application.Services;using Volo.Abp.Caching;using Volo.Abp.Json;namespace Test.Common{    ///     /// HTTP请求实现    ///     public class HttpClientUtils: ApplicationService, IHttpClientUtils    {        private readonly ILogger _logger;        private readonly IDistributedCache> _cache;        public HttpClientUtils(            ILogger logger,            IDistributedCache> cache)         {            _cache = cache;            _logger = logger;        }        ///         /// 发送HTTP请求        ///         /// HttpMethod.Get、HttpMethod.Post、HttpMethod.Put、HttpMethod.Delete        /// 完整地址        /// 请求体对象        /// 鉴权请求头缓存key        /// 请求头        ///         public async Task SendAsync(HttpMethod method, string url, object model = null, string cacheKey = "", Dictionary headers = null)        {            _logger.LogInformation($"SendAsync {method.Method} url = {url}, cacheKey = {cacheKey}, data = {JsonConvert.SerializeObject(model)}");            try            {                using (var client = new HttpClient())                {                    if (!url.StartsWith("http", StringComparison.OrdinalIgnoreCase))                    {                         url = $"http://{url}";                    }                    using (var request = new HttpRequestMessage(method, url))                        {                        if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))                        {                            SetHttps();                        }                        var sc = new StreamContent(new MemoryStream());                        if (model != null)                        {                            var jsonStr = JsonConvert.SerializeObject(model);                            var bytes = Encoding.UTF8.GetBytes(jsonStr);                            sc = new StreamContent(new MemoryStream(bytes));                        }                        HeaderHandler(request, sc.Headers, "application/json", headers, cacheKey);                        request.Content = sc;                        var rsp = await client.SendAsync(request);                        return ResponseHandler(rsp);                    }                }            }            catch (Exception e)            {                _logger.LogError($"SendAsync {method.Method} 请求:{url}, 错误消息:{e.Message}, 请求参数:{JsonConvert.SerializeObject(model)}");                throw new Exception($"请求HTTP服务{new Uri(url).AbsolutePath}异常:{e.Message}");            }        }                ///         /// 发送HTTP表单请求        ///         /// HttpMethod.Post、HttpMethod.Put        /// 完整地址        /// 请求体对象        /// 鉴权请求头缓存key        /// 请求头        ///         public async Task SendFormAsync(HttpMethod method, string url, object model, string cacheKey = "", Dictionary headers = null)        {            _logger.LogInformation($"SendFormAsync {method.Method} url = {url}, cacheKey = {cacheKey}, data = {JsonConvert.SerializeObject(model)}");            try            {                var client = new RestClient();                var request = new RestRequest(url, Method.POST);                if (method == HttpMethod.Put)                {                    request.Method = Method.PUT;                }                                SetHeader(request, cacheKey, headers);                request.AlwaysMultipartFormData = true;                var props = model.GetType().GetProperties();                foreach (var item in props)                {                    var k = item.Name;                    var v = item.GetValue(model);                    if (v == null)                    {                        _logger.LogInformation($"SendFormAsync {method.Method} url = {url} {k} value is null");                        continue;                    }                    if (v.GetType().Name == "Dictionary`2") // 文件只支持字典类型                    {                        if (v is Dictionary)                         {                            var files = v as Dictionary;                            foreach (var obj in files) // 多个文件只能一个个添加,不能集合到List中                            {                                string ext = Path.GetExtension(obj.Key);                                ext = ext.RemovePreFix(".");                                _logger.LogInformation($"SendFormAsync file key = {k}, name = {obj.Key}, ext = {ext}, size = {obj.Value.Length}");                                request.AddFileBytes(k, obj.Value, obj.Key, $"image/{ext}");                            }                        }                    }                    else                    {                        request.AddParameter(k, v);                    }                }                var response = await client.ExecuteAsync(request);                if (response.StatusCode != HttpStatusCode.OK && response.StatusCode != HttpStatusCode.Created)                {                    _logger.LogError($"SendFormAsync {method.Method} {url} 响应失败: {response.Content}");                    throw new Exception($"服务器响应失败:status = {response.StatusCode}");                }                _logger.LogInformation($"SendFormAsync {method.Method} {url} Response: {response.Content}");                return JsonConvert.DeserializeObject(response.Content, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });            }            catch (Exception e)            {                _logger.LogError($"SendFormAsync {method.Method} 请求:{url}, 错误消息:{e.Message}, 请求参数:{JsonConvert.SerializeObject(model)}");                throw new Exception($"请求HTTP服务{new Uri(url).AbsolutePath}异常:{e.Message}");            }        }        ///         /// 设置请求头        ///         ///         ///         private void SetHeader(RestRequest request, string cacheKey, Dictionary headers)        {            // 鉴权请求头            var cacheHeaders = _cache.Get(cacheKey);            if (cacheHeaders != null)            {                foreach (var dic in cacheHeaders)                {                    _logger.LogInformation($"SetHeader cache header: {dic.Key} = {dic.Value}");                    if (dic.Key.Equals("authorization", StringComparison.OrdinalIgnoreCase))                    {                        request.AddHeader("Authorization", dic.Value);                    }                    else                    {                        request.AddHeader(dic.Key, dic.Value);                    }                }            }            // 默认请求头            if (headers != null && headers.Count > 0)            {                foreach (var dic in headers)                {                    request.AddHeader(dic.Key, dic.Value);                }            }        }        ///         /// 处理请求头        ///         /// new HttpRequestMessage        /// new MultipartFormDataContent        /// multipart/form-data        /// Dictionary        /// authorization        private void HeaderHandler(HttpRequestMessage request, HttpContentHeaders contentHeader, string contentType, Dictionary headers, string cacheKey)        {            // 添加默认请求头            if (headers != null && headers.ContainsKey("Content-Type"))            {                contentType = headers["Content-Type"];            }            if (string.IsNullOrEmpty(contentType))             {                contentType = "application/json"; // 默认application/json            }            _logger.LogInformation($"HeaderHandler {request.Method.Method} {request.RequestUri.AbsolutePath} default contentType header: {contentType}");            if (contentType.IndexOf("multipart/form-data") != -1)            {                contentHeader.Remove("Content-Type"); // MediaTypeHeaderValue不支持解析boundary,所以先删除再Add                contentHeader.TryAddWithoutValidation("Content-Type", contentType);            }            else            {                contentHeader.ContentType = new MediaTypeHeaderValue(contentType);             }            // 添加鉴权请求头            var cacheHeaders = _cache.Get(cacheKey);            if (cacheHeaders != null)            {                foreach (var dic in cacheHeaders)                {                    _logger.LogInformation($"HeaderHandler {request.Method.Method} {request.RequestUri.AbsolutePath} cache header: {dic.Key} = {dic.Value}");                    if (dic.Key.Equals("authorization", StringComparison.OrdinalIgnoreCase))                    {                        request.Headers.Authorization = new AuthenticationHeaderValue(dic.Value);                        // request.SetBearerToken(dic.Value); // 这个方法里加了前缀Bearer,针对接口不同                    }                    else                    {                        contentHeader.Add(dic.Key, dic.Value); // cookie、token ...                    }                }            }            // 添加参数请求头            if (headers != null && headers.Count > 0)            {                foreach (var dic in headers)                {                    if (contentHeader.ContentType.MediaType == dic.Value)                    {                        continue;                    }                    contentHeader.Add(dic.Key, dic.Value);                }            }        }        ///         /// 处理HTTP响应        ///         ///         ///         ///         ///         private T ResponseHandler(HttpResponseMessage rsp)        {            if (rsp.StatusCode != HttpStatusCode.OK && rsp.StatusCode != HttpStatusCode.Created)            {                _logger.LogError($"ResponseHandler {rsp.RequestMessage.Method.Method} {rsp.RequestMessage.RequestUri.AbsoluteUri} 响应失败: {rsp.Content.ReadAsStringAsync().Result}");                throw new Exception($"服务器响应失败:status = {rsp.StatusCode}");            }            if (rsp is T)            {               return (T)(object)rsp;  // 返回HttpResponseMessage,用于获取响应头            }            else            {                var json = rsp.Content.ReadAsStringAsync().Result;                _logger.LogInformation($"ResponseHandler {rsp.RequestMessage.Method.Method} {rsp.RequestMessage.RequestUri.AbsoluteUri} 响应:{json}");                return JsonConvert.DeserializeObject(json, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });                // return rsp.Content.ReadFromJsonAsync().Result; // 会检查int null类型报错            }        }        ///         /// 设置HTTPS        ///         private void SetHttps()        {            // set remote certificate Validation auto pass            ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(RemoteCertificateValidate);            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;            // FIX:修复不同.Net版对一些SecurityProtocolType枚举支持情况不一致导致编译失败等问题,这里统一使用数值            //ServicePointManager.SecurityProtocol = (SecurityProtocolType)48 | (SecurityProtocolType)3072 | (SecurityProtocolType)768 | (SecurityProtocolType)192;        }        ///         /// 远程证书验证        ///         ///         ///         ///         ///         /// 验证是否通过,始终通过        private bool RemoteCertificateValidate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)        {            return true;        }    }}
三、遇到问题

1、无法添加请求头content-type

var s = new HttpRequestMessage()s.Headers.Add("Content-Type", "")报错:Misused header name, "Content-Type". Make sure request headers are used with HttpRequestMessage,

var b = new HttpContentHeader()b.Add("Content-Type", "")报错:Cannot add value because header "Content-Type" does not support multiple values.

var b = new HttpContentHeader()b.ContentType = new MediaTypeHeaderValue("multipart/form-data; boundary=----8db27a5c21ea4f8");报错:The format of value "multipart/form-data; boundary=----8db27a5c21ea4f8" is invalid报错:"multipart/form-data; ----8db27a507065123"is invalid报错:"multipart/form-data; boundary=--8db27ca4cfafb57"is invalid

1、解决方案:

请看方法:HeaderHandler

关键词:

C# abp框架Http辅助类 今日讯

C abphttp辅助类文件上传鉴权请求头2023-04-25

观热点:Leader焕新之道:重构品牌与用户的关系

而Leader看到了这一变化,从而重构品牌角色,做新世代的生活养成伙伴2023-04-25

焦点快报!注意!郑煤机将于5月25日召开股东大会

郑煤机(SH601717,收盘价:13 4元)4月25日发布公告称,2023年5月25日9:00,公司将在河南自贸试验区郑州片2023-04-25

前卫设计赋能捷豹路虎新现代豪华

2023上海国际车展上,生于豪华也在重塑豪华的捷豹路虎,以锐意进取之姿再度生动演绎“新现代豪华主义”的独2023-04-25

七匹狼:一季度净利8897.43万元 同比增96.63%-环球短讯

金融界4月25日消息七匹狼披露一季报,一季度实现营业收入9 09亿元,同比增长2 75%;净利润8897 43万元,同2023-04-25

港股收评:恒生科技指数跌3%,医药、芯片、新能源板块下挫 全球快播报

恒指收跌1 71%,恒生科技指数收跌3 46%。2023-04-25

s150号商品报价动态(2023-04-25)

交易商品牌 产地交货地最新报价s150号 优品级济南鑫飞御翔商贸有限公司国产山东省 济南市8000元 吨济南鑫2023-04-25

暴雪欠款3亿遭网易起诉 / VGtime编辑全员离职

据新浪科技报道,近日上海网之易网络科技发展有限公司就暴雪娱乐有限公司违反系列许可协议在上海提起诉讼,2023-04-25

绿色旅游发展大会全力赋能乡村振兴

绿色旅游发展大会全力赋能乡村振兴2023-04-25

哈姆:詹姆斯会努力找到对球队最好的方式 而不是按照他的方式 天天热文

哈姆:詹姆斯会努力找到对球队最好的方式而不是按照他的方式,他的方式,足球竞赛,瑞典足球,湖人主场,米娅·2023-04-25

数字服务应用专委会发布“数智出海”项目:构建互联互通外贸新生态_当前滚动

据连连国际联席CEO吕蔚嬿介绍,“数智出海”项目由连连数字旗下连连国际发起,将聚焦构建“商机互推、资源2023-04-25

今日黄金td行情分析(2023年4月25日)_快讯

今日黄金td行情分析(2023年4月25日)2023-04-25

阳的古诗句有哪些 古人赞美太阳的诗句有哪些?

抄写作文网小编为大家提供阳的古诗句有哪些古人赞美太阳的诗句有哪些?来供大家参考,欢迎阅读。6 描写花草2023-04-25

热点评!“2030年前 中国人脚印定踏上月球”

据大公报报导,中国人何时能够踏足月球,是公众最关注的航天问题。在第八个中国航天日来临之际,中国探月工2023-04-25

在线消费ETF(159728)再度走强涨近2%,巨人网络、掌趣科技、宝通科技、蓝色光标全面上扬

在线消费ETF(159728)再度走强涨近2%,巨人网络、掌趣科技、宝通科技、蓝色光标全面上扬2023年04月25日11:062023-04-25

2023/24年半裙趋势之面料篇-当前聚焦

面料是服装最表层的材料,任何服装都是通过对面料的选用、裁剪、制作等工艺来达到最终的效果。春夏季节,裙2023-04-25

财面儿丨珠海万达商管第三份招股书失效 天天时讯

财经网讯4月25日,据港交所显示,珠海万达商业管理集团股份有限公司(以下简称:珠海万达商管)招股书显示2023-04-25

环球观察:越俎代庖的读音和解释_越俎代庖的读音

1、俎代庖(yuèzǔdàipáo)越:超过。2、俎:古代祭祀时盛牛羊祭品的器具。3、代:代替。4、庖:厨2023-04-25

夏季穿搭不用发愁,这组时尚搭配简约不挑人,轻松穿出好气质-全球快报

到了夏天以后,是否有很多女性朋友会陷入搭配苦恼,觉得每天不知道该怎么打扮才好呢?其实夏季的穿搭不用多2023-04-25

今日视点:年内大涨161%!中科曙光股价续创新高,多路资金热捧

截至发稿,中科曙光获主力资金净买入6 63亿元,净流入额位居A股个股首位。2023-04-25

LVMH市值跻身全球前十! 距超越特斯拉(TSLA.US)仅“一步之遥”_天天速看

在全球市值最大规模公司的排行榜上,LVMH正在逼近全球电动汽车领导者特斯拉。2023-04-25

环球速讯:漯河市源汇区财政局 提升五星“闪亮度” 照亮乡村振兴路

【中原经济网讯】(河南经济报记者刘学中通讯员张丽宋书慧)近年来,漯河市源汇区财政局注重将美丽乡村建设与2023-04-25

要闻速递:事关8号线二期、9号线二期,这几条地铁线路进四期危险了!

撰文:乔治美编:若菡校对:紫藤——1——3号线即将通车,四期还会远吗?4月12日,郑州市城乡建设局官网发2023-04-25

中国妇基会与联想控股持续帮扶榆树市草编创业女性 共同助力乡村产业振兴 天天快资讯

乡村振兴是党的十九大提出的重大战略,是新时代做好 "三农 "工作的总抓手。2023年中央一号文件在部署全面推2023-04-25

投行担心扎克伯格将公司名改为 MetAI,人工智能成公司最大支出 环球热消息

【ITBEAR科技资讯】4月25日消息,就在18个月前,Facebook母公司首席执行官马克·扎克伯格把未来押在了虚拟2023-04-25