技术经验解读:使用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("

相关文章
|
算法 Java 调度
|
Go Android开发
autojs发送通知修改图标
牙叔教程 简单易懂
810 0
|
设计模式 缓存 Java
认真学习设计模式之建造者模式(Builder Pattern)
认真学习设计模式之建造者模式(Builder Pattern)
206 1
|
11月前
|
安全 API 网络架构
Refit使用入门
本文介绍了如何使用Refit库在.NET Core项目中实现RESTful API的调用。通过创建`IGitHubApi`接口定义API方法,并在`Program.cs`中配置Refit客户端,最后在`WeatherForecastController`中演示了两种调用API的方式,展示了Refit的便捷性和类型安全性。
174 1
|
NoSQL Java Redis
认证服务---整合短信验证码,验证码倒计时,验证码防刷校验 【一】
这篇文章介绍了如何在分布式微服务项目中整合短信验证码服务,包括使用阿里云短信验证接口、将短信验证功能集成到第三方服务中、其他服务的远程调用,以及通过Redis实现验证码防刷机制的代码实现和遇到的问题解决方案。
|
12月前
|
SQL 消息中间件 存储
小象超市(原美团买菜) 的大屏图表
小象超市(原美团买菜) 的大屏图表
475 0
|
SQL 存储
如何在 SQL Server 中使用 `OUTPUT` 子句
【8月更文挑战第10天】
446 7
如何在 SQL Server 中使用 `OUTPUT` 子句
|
应用服务中间件
解决在启动tomcat出现的The CATALINA_HOME environment variable is not defined correctly问题~
解决在启动tomcat出现的The CATALINA_HOME environment variable is not defined correctly问题~
1586 0
|
监控 Unix Java
使用Nohup命令管理后台进程的实用技巧
使用Nohup命令管理后台进程的实用技巧
|
开发框架 NoSQL 关系型数据库
基于SqlSugar的开发框架循序渐进介绍(27)-- 基于MongoDB的数据库操作整合
基于SqlSugar的开发框架循序渐进介绍(27)-- 基于MongoDB的数据库操作整合