众所周知,微服务架构是由一众微服务组成,项目中调用其他微服务接口更是常见的操作。为了便于调用外部接口,我们的常用思路一般都是封装一个外部接口的客户端,使用时候直接调用相应的方法。webservice或WCF的做法就是引用服务,自动生成客户端。在webapi2.0里,我们都会手动封装一个静态类。那么在.netcore3.1的微服务时代,我们该如何处理这个问题呢? ----思路都是一样的,封装一个外部服务,并且使用依赖注入和 HttpFactory工厂等.netcore特有的方式提升性能。接下来我们一步一步说下详细的步骤:
第1步:--创建项目
为了便于构建生成nuget包,我们一般都每个外部服务创建一个独立的项目;如下图:
在解决方案里,我们创建了一个项目名为:"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; } }
该段代码虽然很短,但是最关键的部分:
代码的执行过程如下:
(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 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; }
原文地址:https://www.cnblogs.com/puzi0315/p/12239442.html