声名式服务调用,己经不算是一个新鲜的话题了,毕竟都出来好些年了。react
下面谈谈,最近项目中用到一个这样的组件的简单实践。git
目前部分项目用到的是Refit这个组件,都是配合HttpClientFactory来使用的。github
关于HttpClientFactory的一些简单介绍,能够参见官方文档,也能够看看前面的两篇比较粗略的相关介绍。json
也简单介绍一下背景,目前主要有两类的API接口:api
第一类是注册到Eureka中的,能够经过服务发现的方式来请求的,这里的都是新的接口。安全
第二类是原始的接口,不能走服务发现,只能经过直连请求的方式来调用,这里的都是些老接口。服务器
换句话就是说,要同时兼容这两类接口。网络
因为用HttpClientFactory集成服务发现十分简单,因此优先选了一个自己就带有HttpClientFactory的组件--Refit。架构
Refit是一个自动类型安全的REST库,是RESTful架构的.NET客户端实现,app
它基于Attribute,提供了把REST API返回的数据转化为(Plain Ordinary C# Object,简单C#对象),POCO to JSON,网络请求(POST,GET,PUT,DELETE等)封装,内部封装使用HttpClient,前者专一于接口的封装,后者专一于网络请求的高效,两者分工协做。
咱们的应用程序经过 refit请求网络,其实是使用 refit接口层封装请求参数、Header、Url 等信息,以后由 HttpClient完成后续的请求操做,在服务端返回数据以后,HttpClient将原始的结果交给 refit,后者根据用户的需求对结果进行解析的过程。
更多细节能够参考Refit的官网
直接上控制器的代码了〜〜
// GET: api/persons [HttpGet] public IEnumerable<Person> Get() { return new List<Person> { new Person{Id = 1 , Name = "catcher wong", CreateTime = DateTime.Now}, new Person{Id = 2 , Name = "james li", CreateTime = DateTime.Now.AddDays(-2)} }; } // GET api/persons/5 [HttpGet("{id}")] public Person Get(int id) { return new Person { Id = id, Name = "name" }; } // POST api/persons [HttpPost] public Person Post([FromBody]Person person) { if (person == null) return new Person(); return new Person { Id = person.Id, Name = person.Name }; } // PUT api/persons/5 [HttpPut] public string Put([FromBody]int id) { return $"put {id}"; } // DELETE api/persons/5 [HttpDelete("{id}")] public string Delete(int id) { return $"del {id}"; }
先经过Nuget安装Refit的包。
而后就是定义咱们的interface了
public interface IPersonsApi { [Get("/api/persons")] Task<List<Person>> GetPersonsAsync(); [Get("/api/persons/{id}")] Task<Person> GetPersonAsync([AliasAs("id")]int personId); [Post("/api/persons")] Task<Person> AddPersonAsync([Body]Person person); [Put("/api/persons")] Task<string> EditPersonAsync([Body]int id); [Delete("/api/persons/{id}")] Task<string> DeletePersonAsync(int id); }
来看看这个interface里面涉及到的部份内容。
而后是配合HttpClientFactory
再经过Nuget安装一下Refit.HttpClientFactory
若是PersonApi是注册到Euerka的,能够再添加Steeltoe的引用。
public void ConfigureServices(IServiceCollection services) { services.AddRefitClient<IPersonsApi>() .ConfigureHttpClient(options => { options.BaseAddress = new Uri(Configuration.GetValue<string>("personapi_url")); //other settings of httpclient }) //Steeltoe discovery //.AddHttpMessageHandler<DiscoveryHttpMessageHandler>() ; services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }
前面在定义IPersonApi的时候,咱们只指定了相对路径,而请求IP并无指定,这里是放到ConfigureHttpClient里面去指定了。
同时根据不一样环境,配置不一样的appsettings.{env}.json,达到切换的效果。
一样的,若是想走服务发现,只须要放开注释的AddHttpMessageHandler,同时修改BaseeAddress为服务名的形式就能够了。
说了这么多,都还只是配置阶段,下面就来看看具体怎么用。
为了演示方便,就不在建一个Service层了,直接在控制器调用一下。
用法也很简单,直接在控制器注入一下就可使用了。
[Route("api/[controller]")] [ApiController] public class ValuesController : ControllerBase { private readonly IPersonsApi _api; public ValuesController(IPersonsApi api) { this._api = api; } // GET api/values [HttpGet] public async Task<List<Person>> GetAsync() { return await _api.GetPersonsAsync(); } // GET api/values/5 [HttpGet("{id}")] public async Task<Person> Get(int id) { return await _api.GetPersonAsync(id); } // POST api/values [HttpPost] public async Task<Person> Post([FromBody] Person value) { return await _api.AddPersonAsync(value); } }
到这里,代码层面的东西已经处理完了。
下面来看看使用Refit效果(这里只看两个Get请求的):
都是能正常拿到咱们指望的结果。
最后再看看输出的日志,确认一下。
首先是访问/api/values
的
确确实实是向咱们前面的PersonApi发起了请求。
而后是访问/api/values/5555
的
可见咱们上面的别名(AliasAs)是起了效果的,能拼成正确的请求地址。
至于其余类型的请求,这里就不演示了,让你们本身去尝试一下吧。
Refit用起来仍是比较简单的,运行了一段时间也还表现正常!
固然本文介绍的也只是一些基本的用法!它还具备不错的扩展性,可让咱们根据自身需求作一些定制化的东西。
本文的示例代码RefitClientApi