本文為原創文章:首發: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=https://www.cnblogs.com/puzi0315/p/{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=https://www.cnblogs.com/puzi0315/p/{paramStr} ,jrclientguid={jrclientguid}---,ex={ex.Message}" ); throw; } finally { _logger.LogInformation($"{funName}結束,url={_client.BaseAddress},action={actionUrl},postData=https://www.cnblogs.com/puzi0315/p/{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 = "https://www.cnblogs.com/puzi0315/p/連接成功"; return result; }
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/67209.html
標籤:.NET Core
上一篇:C#里面低消耗獲取當前時間的思路
