zoukankan      html  css  js  c++  java
  • Refit在ASP.NET Core中的实践

    前言

    声名式服务调用,己经不算是一个新鲜的话题了,毕竟都出来好些年了。

    下面谈谈,最近项目中用到一个这样的组件的简单实践。

    目前部分项目用到的是Refit这个组件,都是配合HttpClientFactory来使用的。

    关于HttpClientFactory的一些简单介绍,可以参见官方文档,也可以看看前面的两篇比较粗略的相关介绍。

    也简单介绍一下背景,目前主要有两类的API接口:

    第一类是注册到Eureka中的,可以通过服务发现的方式来请求的,这里的都是新的接口。

    第二类是原始的接口,不能走服务发现,只能通过直连请求的方式来调用,这里的都是些老接口。

    换句话就是说,要同时兼容这两类接口。

    由于用HttpClientFactory集成服务发现十分简单,所以优先选了一个本身就带有HttpClientFactory的组件--Refit。

    什么是Refit

    Refit是一个自动类型安全的REST库,是RESTful架构的.NET客户端实现,

    它基于Attribute,提供了把REST API返回的数据转化为(Plain Ordinary C# Object,简单C#对象),POCO to JSON,网络请求(POST,GET,PUT,DELETE等)封装,内部封装使用HttpClient,前者专注于接口的封装,后者专注于网络请求的高效,二者分工协作。

    我们的应用程序通过 refit请求网络,实际上是使用 refit接口层封装请求参数、Header、Url 等信息,之后由 HttpClient完成后续的请求操作,在服务端返回数据之后,HttpClient将原始的结果交给 refit,后者根据用户的需求对结果进行解析的过程。

    更多细节可以参考Refit的官网

    创建一个可调用的API接口

    直接上控制器的代码了〜〜

    // 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}";
    }
    

    Refit的使用

    先通过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里面涉及到的部分内容。

    1. Get,Post等特性就表明了接口的请求方式,后面的值就是请求的相对路径。
    2. 相对路径中,可以使用占位符,来动态更新参数值。
    3. 如果方法名和请求参数名不一致,需要用AliasAs指明。
    4. 通过Body特性声明一个对象作为请求体发送到服务器
    5. 返回值定义是Task或者IObservable

    然后是配合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

  • 相关阅读:
    java fastJson
    动态 商品属性
    添加营业时间
    ivew 表格中的input数据改变就会失去焦点
    小程序-setData
    小程序 css3走马灯效果
    iview 表单验证
    vue iview tree checked改变 不渲染的问题
    pl/sql中文乱码
    sql-plus无法连接解决
  • 原文地址:https://www.cnblogs.com/catcher1994/p/9501387.html
Copyright © 2011-2022 走看看