• .netcore 3.1高性能微服务架构:封装调用外部服务的接口方法--HttpClient客户端思路分析


    本文为原创文章:首发:http://www.zyiz.net/

    众所周知,微服务架构是由一众微服务组成,项目中调用其他微服务接口更是常见的操作。为了便于调用外部接口,我们的常用思路一般都是封装一个外部接口的客户端,使用时候直接调用相应的方法。webservice或WCF的做法就是引用服务,自动生成客户端。在webapi2.0里,我们都会手动封装一个静态类。那么在.netcore3.1的微服务时代,我们该如何处理这个问题呢? ----思路都是一样的,封装一个外部服务,并且使用依赖注入和 HttpFactory工厂等.netcore特有的方式提升性能。接下来我们一步一步说下详细的步骤:

    第1步:--创建项目

    为了便于构建生成nuget包,我们一般都每个外部服务创建一个独立的项目;如下图:

    在解决方案里,我们创建了一个项目名为:"MuXue.Zyiz.Template.OuterClient",(项目起名一般为 公司名.部门名.项目名.xxx)。

    第2步:创建一个IServiceCollection扩展服务,便于将服务注册信息。(重点)

        public static  class MsgApiClientServiceCollectionExtensions
        {
            public static IServiceCollection AddMsgApiClient(this IServiceCollection services, IConfiguration MsgClientConfiguration)
            {
                services.Configure<MsgClientConfiguration>(MsgClientConfiguration)
                        .AddHttpClient<IMsgApiClient, MsgApiClient>()
                        .ConfigureHttpClient(config=> {
                            config.BaseAddress = new Uri(MsgClientConfiguration.GetSection("url").Value);
                            config.Timeout = TimeSpan.FromSeconds(30);
                            });
    
                return services;
            }
        }

    该段代码虽然很短,但是最关键的部分:

    代码的执行过程如下:

    (1)  services首先注册一个操作配置文件的实例 :

    services.Configure<MsgClientConfiguration>(MsgClientConfiguration)

    (2) 添加HttpClientFactory工厂并且关联到services里,并将Client强制类型为IMsgApiClient(自定义的外部微服务接口名称) :

    .AddHttpClient<IMsgApiClient, MsgApiClient>();//IMsgApiClient为接下来要创建的客户端

    (3)设置HttpClient的相关配置参数:

     .ConfigureHttpClient(config=> {
                            config.BaseAddress = new Uri(MsgClientConfiguration.GetSection("url").Value);//外部微服务接口域名
                            config.Timeout = TimeSpan.FromSeconds(30);  // 接口调用超时时间
                            });

    还有如下注意点:

    (1)以静态类和静态方法方式;

      (2) this IServiceCollection services ,这个参数我就不多解释了。

     (3) 一般我们都需要读取appsettings.json配置文件里的参数所以这里接收了一个参数---IConfiguration MsgClientConfiguration  ;当然如果你不需要读取配置参数,也可以忽略这个参数;

    第3步:写外部接口调用的具体逻辑代码:

    (1)创建一个接口,比如IMsgApiClient

     /// <summary>
        /// 企业微信消息发送客户端
        /// </summary>
        public interface IMsgApiClient
        {
    
            /// <summary>
            /// 调用接口接口:向微信发送消息
            /// </summary>
            /// <param name="hrcodeStr">hrcode,多个以|隔开</param>
            /// <param name="msg">消息内容</param>
            /// <returns></returns>
            Task<Result<string>> SendWxWorkMsgAsync(string hrcodeStr, string msg);
    
        }

    (2)实现该接口:

      /// <summary>
            /// 调用外部接口:向SD发送消息
            /// </summary>
            /// <param name="hrcodeStr">hrcode,多个以|隔开</param>
            /// <param name="msg">消息内容</param>
            /// <returns></returns>
            public async Task<Result<string>> SendWxWorkMsgAsync(string hrcodeStr, string msg)
            {
                Result<string> result = new Result<string>();
                string funName = "【调用外部接口:微信企业消息推送接口】";
                try
                { 
                     //具体的业务逻辑---根据自身业务来写
                    MsgApiResult<WeiXinWorkMessageResponse> sendRet = await ZyizHttpClientExtensions.PostData<MsgApiResult<WeiXinWorkMessageResponse>>(_client, _logger, "/api/weixin/work/messages/send", msgReq);
    
                    if (sendRet != null)
                    {
                        _logger.LogInformation($"{funName},{hrcodeStr}推送消息成功");
                        result.state = true;
                        result.data = sendRet.Return_data.MessageId;
                    }
                    else
                    {
                        result.state = false;
                        result.error_msg = sendRet.Return_msg;
                        _logger.LogError($"{funName}:{hrcodeStr}推送消息失败:{sendRet.Return_msg}");
                    }
    
                }
                catch (Exception ex)
                {
                    result.state = false;
                    result.error_code = ErrorCode.OuterApiError;
                    result.error_msg = funName + "调用外部接口异常:。" + ex.Message;
                    _logger.LogError(ex, $"{funName}向{hrcodeStr}推送消息处理异常:{ex.Message}");
                }
    
    
                return result;
    
            }

    针对HttpClient的Post方法我特意封装了一个通用方法,如下:(可以根据自身项目自行改造)

     /// <summary>
       /// HttpClient扩展方法
       /// </summary>
        public class ZyizHttpClientExtensions
        {
     
            /// <summary>
            /// httpclient-post方法的简单处理,封装成一个方法,便于调用
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="_client"></param>
            /// <param name="_logger"></param>
            /// <param name="actionUrl">http://xxx.com/后面之后的地址</param>
            /// <param name="param">一个对象</param>
            /// <param name="ContentType"></param>
            /// <param name="BearerToken"></param>
            /// <returns></returns>
            public async static  Task<T> PostData<T>(HttpClient _client, ILogger _logger,string actionUrl, dynamic param, string ContentType = "application/json", string BearerToken = "")
            {
                string funName = "PostData";
                string paramStr = JsonConvert.SerializeObject(param);
                string jrclientguid = Guid.NewGuid().ToString("n");
                try
                {
                    _logger.LogInformation($"{funName}开始,url={_client.BaseAddress},action={actionUrl},postData={paramStr} ,jrclientguid={jrclientguid}---------");
    
                    HttpResponseMessage response;
                    using (HttpContent httpContent = new StringContent(paramStr, Encoding.UTF8))
                    {
                        if (!string.IsNullOrWhiteSpace(BearerToken))
                        {
                            _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", BearerToken);
                        }
    
                        httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(ContentType);
    
                        response = await _client.PostAsync(actionUrl, httpContent);
    
                    }
                    if (response != null && response.IsSuccessStatusCode)
                    {
                        Type t = typeof(T);
                        if (typeof(T) == typeof(string))
                        {
                            string respStr = await response.Content.ReadAsStringAsync();
                            return (T)Convert.ChangeType(respStr, typeof(T));
                        }
                        else
                        {
                            string respStr = response.Content.ReadAsStringAsync().Result;
                            T resp = JsonConvert.DeserializeObject<T>(respStr);
    
                            return resp;
                        }
                    }
                    else
                    {
                        return default(T);
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex,$"{funName}报错啦,url={_client.BaseAddress},action={actionUrl},postData={paramStr} ,jrclientguid={jrclientguid}---,ex={ex.Message}" );
                    throw;
                }
                finally
                {
                    _logger.LogInformation($"{funName}结束,url={_client.BaseAddress},action={actionUrl},postData={paramStr} ,jrclientguid={jrclientguid}---------");
                }
    
    
            }
    
    
        }

    第4步:将服务注入容器:代码在Startup类的ConfigureServices方法里:

     services.AddZyizMVC(Env)
                   .AddZyizDbContext(Configuration)
    .AddMsgApiClient(Configuration.GetSection(nameof(MsgApiClient)))//就是这行

    第5步:使用:在Controller或其他地方都可以使用;

    (1)首先在构造函数里注册:

      private readonly ILogger _logger;
            private IMsgApiClient _IMsgApiClient;
    
            public HealthController(ILogger<HealthController> logger,    IMsgApiClient iMsgApiClient)
            {
                _logger = logger;
                _IMsgApiClient = iMsgApiClient;
    
            }

    (2)调用方法:

     /// <summary>
            /// 发消息
            /// </summary>
            /// <returns></returns>
            [HttpGet]
            public async Task<Result<string>> POk()
            { 
                Result<string> result = new Result<string>();
                result = await _IMsgApiClient.SendWxWorkMsgAsync("100001002", "我是沐雪,请明天来我办公室一趟!");
                //result.state = true;
                //result.data = "连接成功";
    
                return result;
            }
  • 相关阅读:
    计算器部分代码
    学写压缩壳心得系列之二 掌握PE结构 ,曲径通幽
    headerfiles
    VC实现文件拖拽
    学写压缩壳心得系列之三 模拟加载,步步为营
    ASPack 2.x (without poly) > Alexey Solodovnikov [Overlay]脱壳
    学写压缩壳心得系列之一 熟悉概念,未雨绸缪
    upx最新壳脱壳测试
    正则表达式大全
    win7 iis http 500 错误
  • 原文地址:https://www.cnblogs.com/puzi0315/p/12239442.html
Copyright © 2020-2023  润新知