.netcore 3.1高性能微服務架構:封裝調用外部服務的接口方法--HttpClient客戶端思路分析

衆所周知,微服務架構是由一衆微服務組成,項目中調用其餘微服務接口更是常見的操做。爲了便於調用外部接口,咱們的經常使用思路通常都是封裝一個外部接口的客戶端,使用時候直接調用相應的方法。webservice或WCF的作法就是引用服務,自動生成客戶端。在webapi2.0裏,咱們都會手動封裝一個靜態類。那麼在.netcore3.1的微服務時代,咱們該如何處理這個問題呢? ----思路都是同樣的,封裝一個外部服務,而且使用依賴注入和 HttpFactory工廠等.netcore特有的方式提高性能。接下來咱們一步一步說下詳細的步驟:web

第1步:--建立項目json

爲了便於構建生成nuget包,咱們通常都每一個外部服務建立一個獨立的項目;以下圖:api

在解決方案裏,咱們建立了一個項目名爲:"JR.UPC.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;
        }
    }

該段代碼雖然很短,可是最關鍵的部分:app

代碼的執行過程以下:async

(1)  services首先註冊一個操做配置文件的實例 :函數

services.Configure<MsgClientConfiguration>(MsgClientConfiguration)

 

(2) 添加HttpClientFactory工廠而且關聯到services裏,並將Client強制類型爲IMsgApiClient(自定義的外部微服務接口名稱) :微服務

.AddHttpClient<IMsgApiClient, MsgApiClient>();//IMsgApiClient爲接下來要建立的客戶端

 

(3)設置HttpClient的相關配置參數:post

 .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 UPCHttpClientExtensions.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 UPCHttpClientExtensions
    {
 
        /// <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.AddUpcMVC(Env)
               .AddUPCDbContext(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;
        }
相關文章
相關標籤/搜索