技术经验解读:使用Refit框架访问REST接口

简介: 技术经验解读:使用Refit框架访问REST接口

Refit 是一个类型安全的 REST 开源库,是一套基于 RESTful 架构的 .NET 客户端实现,内部使用 HttpClient 类封装,可通过 Refit 更加简单安全地访问 Web API 接口,要使用 Refit 框架,只需要在项目中通过 NuGet 包安装器安装即可。


Install-Package refit


使用方法很简单:


public interface IGitHubApi


{


【Get("/users/{userid}")】


Task GetUser(string userid);


}


以上方法定义一个 REST API 接口,该接口定义了 GetUser 函数,该函数通过 HTTP GET 请求去访问服务器的 /users/{userid} 路径并把返回的结果封装为 User 对象返回,其中 URL 路径中 {userid} 的值为 GetUser 函数的 userid 参数取值,然后,通过 RestService 类生成 IGitHubApi 的代理实现,通过代理直接调用 Web API 接口。


var gitHubApi = RestService.For("");


var octocat = await gitHubApi.GetUser("xcode");


API Attributes特性


通过 Attribute 特性标记,指定请求方法和相对 URL 地址,内置支持 Get、Post、Put、Delete 和 Head 方法。


【Get("/users/list")】


也可以在 URL 中指定查询参数:


【Get("/users/list?sort=desc")】


方法中的 URL 地址可以使用占位符,占位符是由 {} 包围的字符串,如果函数参数与 URL 路径中的占位符名称不同,可使用 AliasAs 指定别名。


【Get("/group/{id}/users")】


Task

值得注意的是,参数名称和 URL 参数占位符不区分大小写,如果一个函数参数未被 URL 占位符所使用,那么它将自动被当作 QueryString 查询字符串来使用


【Get("/group/{id}/users")】


Task

当我们调用 GroupList 方法时,相当于请求 "/group/4/users?sort=desc"这个地址,其中 sort 参数被当作 GET 参数自动使用。


Dynamic Querystring Parameters


函数参数可传递对象,对象的字段属性将被自动追加到 Querystring 查询字符串。


public class MyQueryParams


{


【AliasAs("order")】


public string SortOrder { get; set; }


public int Limit { get; set; }


}


【Get("/group/{id}/users")】


Task

【Get("/group/{id}/users")】


Task

param.SortOrder = "desc";


param.Limit = 10;


GroupList(4, param)



GroupListWithAttribute(4, param)



Collections as Querystring parameters


除了支持对象参数外,还是支持集合参数,下面是使用示例:


【Get("/users/list")】


Task Search(【Query(CollectionFormat.Multi)】int【】 ages);


Search(new 【】 {10, 20, 30})



【Get("/users/list")】


Task Search(【Query(CollectionFormat.Csv)】int【】 ages);


Search(new 【】 {10, 20, 30})



Body内容


通过使用 BodyAttribute 特性,将函数参数追加到 HTTP 请求的 Body 部分。


【Post("/users/new")】


Task CreateUser(【Body】 User user);


根据参数的类型,提供 Body 数据有四种可能:如果类型为 Stream 流类型,则内容将通过 StreamContent 流式传输。如果类型是 String 字符串类型,则该字符串将直接用作内容。如果参数具有 【Body(BodySerializationMethod.UrlEncoded)】 属性,内容将被 URL 编码后使用。对于以上除外的其它类型,对象将被序列化为 JSON 传输。


JSON内容


基于 JSON 的请求和响应,内部使用 JSON.NET 框架进行序列化和反序列化,默认情况下,Refit 框架将使用 JsonConvert.DefaultSettings 来配置序列化器的行为:


JsonConvert.DefaultSettings =


() => new JsonSerializerSettings() {


ContractResolver = new CamelCasePropertyNamesContractResolver(),


Converters = {new StringEnumConverter()}


};


// Serialized as: {"day":"Saturday"}


await PostSomeStuff(new { Day = DayOfWeek.Saturday });


因为静态属性 DefaultSettings 是全局设置,它会影响整个应用程序,有些时候,我们希望对某些 API 请求使用特定序列化设置,可以使用 RefitSettings 来指定。


var gitHubApi = RestService.For("",


new RefitSettings {


JsonSerializerSettings = new JsonSerializerSettings {


ContractResolver = new SnakeCasePropertyNamesContractResolver()


}


});


var otherApi = RestService.For("",


new RefitSettings {


JsonSerializerSettings = new JsonSerializerSettings {


ContractResolver = new CamelCasePropertyNamesContractResolver()


}


});


对象属性的序列化行为可以通过 JSON.NET 框架本身的 JsonPropertyAttribute 特性定制:


public class Foo


{


// Works like 【AliasAs("b")】 would in form posts (see below)


【JsonProperty(PropertyName="b")】


public string Bar { get; set; }


}


Form posts


对于采用表单提交数据(application/x-www-form-urlencoded)的 API 接口,使用 BodySerializationMethod.UrlEncoded 初始化 BodyAttribute 特性,参数可以是一个 IDictionary 字典。


public interface IMeasurementProtocolApi


{


【Post("/collect")】


Task Collect(【Body(BodySerializationMethod.UrlEncoded)】 Dictionary[span style="color: rgba(0, 0, 255, 1)">string, object

}


var data = new Dictionary[span style="color: rgba(0, 0, 255, 1)">string, object

{"v", 1},


{"tid", "UA-1234-5"},


{"cid", new Guid("d1e9ea6b-2e8b-4699-93e0-0bcbd26c206c")},


{"t", "event"},


};


// Serialized as: v=1&tid=UA-1234-5&cid=d1e9ea6b-2e8b-4699-93e0-0bcbd26c206c&t=event


await api.Collect(data);


通过表单提交传递数据,也可以是任何对象,对象的所有公开属性和字段将被序列化,可使用 AliasAs 指定别名:


public interface IMeasurementProtocolApi


{


【Post("/collect")】


Task Collect(【Body(BodySerializationMethod.UrlEncoded)】 Measurement measurement);


}


public class Measurement


{


// Properties can be read-only and 【AliasAs】 isn't required


public int v { get { return 1; } }


【AliasAs("tid")】


public string WebPropertyId { get; set; }


【AliasAs("cid")】


public Guid ClientId { get; set; }


【AliasAs("t")】


public string Type { get; set; }


public object IgnoreMe { private get; set; }


}


var measurement = new Measurement {


WebPropertyId = "UA-1234-5",


ClientId = new Guid("d1e9ea6b-2e8b-4699-93e0-0bcbd26c206c"),


Type = "event"


};


// Serialized as: v=1&tid=UA-1234-5&cid=d1e9ea6b-2e8b-4699-93e0-0bcbd26c206c&t=event


await api.Collect(measurement);


设置静态请求头


您可以使用 HeadersAttribute 特性设置一个或多个 HTTP 静态请求标头:


【Headers("User-Agent: Awesome Octocat App")】


【Get("/users/{user}")】


Task GetUser(string user);


也可以通过将 HeadersAttribute 特性应用于接口,这将影响该接口中的所有请求方法:


【Headers("User-Agent: Awesome Octocat App")】


public interface IGitHubApi


{


【Get("/users/{user}")】


Task GetUser(string user);


【Post("/users/new")】


Task CreateUser(【Body】 User user);


}


设置动态请求头


如果请求头需要在运行时设置,则可以通过将 HeaderAttribute 特性应用到函数参数,从而为请求头添加动态值。


【Get("/users/{user}")】


Task GetUser(string user, 【Header("Authorization")】 string authorization);


// Will add the header "Authorization: token OAUTH-TOKEN" to the request


var user = await GetUser("octocat", "token OAUTH-TOKEN");


授权(动态请求头)


标头最常见的用途是授权,今天,大多数 API 都使用 oAuth 协议通过访问令牌授权,申请访问令牌,即可访问 API 接口,访问令牌到期后需要刷新令牌,取得更长寿命的令牌,封装这些令牌的操作,可通过自定义 HttpClientHandler 来实现:


class AuthenticatedHttpClientHandler : HttpClientHandler


{


private readonly Funcstring getToken;


public AuthenticatedHttpClientHandler(Funcstring getToken)


{


if (getToken == null) throw new ArgumentNullException(nameof(getToken));


this.getToken = getToken;


}


protected //代码效果参考:http://www.jhylw.com.cn/112822701.html

override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

{


// See if the request has an authorize header


var auth = request.Headers.Authorization;


if (auth != null)


{


var token = await getToken().ConfigureAwait(false);


request.Headers.Authorization = new AuthenticationHeaderValue(auth.Scheme, token);


}


return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);


}


}


虽然 HttpClient 包含几乎相同的方法签名, 但使用方式不同,HttpClient.SendAsync 没有被改装,必须改为修改 HttpClientHandler,像这样使用:


class //代码效果参考:http://www.jhylw.com.cn/285424335.html

LoginViewModel

{


AuthenticationContext context = new AuthenticationContext(...);


private async Task[span style="color: rgba(0, 0, 255, 1)">string

{


// The AcquireTokenAsync call will prompt with a UI if necessary


// Or otherwise silently use a refresh token to return


// a valid access token


var token = await context.AcquireTokenAsync("", "clientId", new Uri(""));


return token;


}


public async void LoginAndCallApi()


{


var api = RestService.For(new HttpClient(new AuthenticatedHttpClientHandler(GetToken)) { BaseAddress = new Uri("

相关文章
|
1月前
|
缓存 测试技术 API
构建高效后端API:实践与哲学
【9月更文挑战第36天】在数字世界的浪潮中,后端API成为了连接用户、数据和业务逻辑的桥梁。本文将深入探讨如何构建一个既高效又灵活的后端API,从设计理念到实际代码实现,带你一探究竟。我们将通过具体示例,展示如何在保证性能的同时,也不失安全性和可维护性。
|
1月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
48 4
|
5天前
|
存储 JSON 测试技术
构建高效后端API:实践和原则
【10月更文挑战第43天】本文深入探讨了如何设计和实现高效、可维护的后端API,强调了设计哲学、最佳实践和常见陷阱。通过具体示例,我们展示了如何运用这些原则来提高API的性能和可用性。
|
22天前
|
前端开发 关系型数据库 API
深入浅出后端开发——从零到一构建RESTful API
本文旨在为初学者提供一个关于后端开发的全面指南,特别是如何从零开始构建一个RESTful API。我们将探讨后端开发的基本概念、所需技术栈、以及通过实际案例展示如何设计和实现一个简单的RESTful API。无论你是完全的新手还是有一定编程基础的开发者,这篇文章都将为你提供实用的知识和技巧,帮助你在后端开发的道路上迈出坚实的一步。
|
22天前
|
缓存 负载均衡 测试技术
‌API开发的基础概念和作用‌
API(Application Programming Interface)是一组定义了软件组件之间交互规则的接口。它提供了一种标准化的方式,让不同的软件组件之间可以进行通信和交互。
|
28天前
|
缓存 监控 API
微服务架构下RESTful风格api实践中,我为何抛弃了路由参数 - 用简单设计来提速
本文探讨了 RESTful API 设计中的两种路径方案:动态路径和固定路径。动态路径通过路径参数实现资源的 CRUD 操作,而固定路径则通过查询参数和不同的 HTTP 方法实现相同功能。固定路径设计提高了安全性、路由匹配速度和 API 的可维护性,但也可能增加 URL 长度并降低表达灵活性。通过对比测试,固定路径在性能上表现更优,适合微服务架构下的 API 设计。
|
2月前
|
JSON 安全 API
构建高效后端API的五大原则
【9月更文挑战第10天】在数字化时代,后端API成为了连接用户与服务、实现数据流动的重要桥梁。本文将深入探讨如何设计并构建一个高效、可扩展且安全的后端API系统。我们将从RESTful架构开始,逐步深入到错误处理、安全性、性能优化和文档编写等方面,为读者提供一套完整的后端API构建指南。无论你是初学者还是有经验的开发者,这篇文章都将为你带来新的视角和思考。
|
3月前
|
存储 设计模式 API
深入浅出后端开发:从零到一构建RESTful API
【8月更文挑战第29天】本文旨在引导读者理解后端开发的精髓,并通过实际代码示例,展示如何从无到有构建一个RESTful API。文章首先解释后端开发的基本概念,然后逐步深入到API设计的原则和最佳实践,最后通过一个具体的代码示例,让读者能够动手实践,体验后端开发的乐趣。
|
2月前
|
存储 JavaScript NoSQL
深入浅出后端开发:构建你的第一个RESTful API
【9月更文挑战第17天】在数字时代的浪潮中,后端开发是支撑起整个互联网的骨架。本文将引导读者了解后端开发的基本概念,并通过一个实际的代码示例,展示如何从零开始构建一个简单的RESTful API。我们将一起探索API设计的哲学、选择合适的后端语言和框架,以及实现数据存储和接口测试的过程。无论你是编程新手,还是希望扩展你的技术栈,这篇文章都将为你提供一次全面而深入的后端开发之旅。
43 0
|
3月前
|
缓存 JavaScript API
构建高性能后端API:从理论到实践
【8月更文挑战第31天】 在数字化浪潮的推动下,后端API的性能成为影响用户体验和应用响应速度的关键因素。本文将带你深入理解高性能API的设计原则和实现方法,通过具体的代码示例展示如何优化API性能,确保你的后端服务能够快速、稳定地处理大量请求。