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

相关文章
|
8月前
|
API 数据库 数据安全/隐私保护
利用Django框架构建高效后端API服务
本文将介绍如何利用Django框架构建高效的后端API服务。通过深入分析Django框架的特性和优势,结合实际案例,探讨了如何利用Django提供的强大功能来构建高性能、可扩展的后端服务。同时,还对Django框架在后端开发中的一些常见问题进行了解决方案的探讨,并提出了一些建设性的建议。
205 3
|
3月前
|
缓存 测试技术 API
构建高效后端API:实践与哲学
【9月更文挑战第36天】在数字世界的浪潮中,后端API成为了连接用户、数据和业务逻辑的桥梁。本文将深入探讨如何构建一个既高效又灵活的后端API,从设计理念到实际代码实现,带你一探究竟。我们将通过具体示例,展示如何在保证性能的同时,也不失安全性和可维护性。
|
3月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
65 4
|
2月前
|
存储 JSON 测试技术
构建高效后端API:实践和原则
【10月更文挑战第43天】本文深入探讨了如何设计和实现高效、可维护的后端API,强调了设计哲学、最佳实践和常见陷阱。通过具体示例,我们展示了如何运用这些原则来提高API的性能和可用性。
|
2月前
|
前端开发 关系型数据库 API
深入浅出后端开发——从零到一构建RESTful API
本文旨在为初学者提供一个关于后端开发的全面指南,特别是如何从零开始构建一个RESTful API。我们将探讨后端开发的基本概念、所需技术栈、以及通过实际案例展示如何设计和实现一个简单的RESTful API。无论你是完全的新手还是有一定编程基础的开发者,这篇文章都将为你提供实用的知识和技巧,帮助你在后端开发的道路上迈出坚实的一步。
|
4月前
|
前端开发 API 数据安全/隐私保护
打造高效后端API:RESTful设计原则与实践
【9月更文挑战第4天】在数字化时代的浪潮中,后端API作为连接数据和前端的桥梁,其设计质量直接影响着应用的性能和扩展性。本文将深入探讨RESTful API的设计哲学、核心原则以及如何在实际开发中应用这些原则来构建一个高效、易于维护的后端系统。我们将通过代码示例,揭示如何将理论转化为实践,从而为开发者提供一条清晰的道路,去创造那些能够在不断变化的技术环境中茁壮成长的API。
|
4月前
|
存储 JSON API
构建高效后端API:实践与思考
【8月更文挑战第31天】本文深入探讨如何打造一个高效、可靠的后端API。我们将通过实际案例,揭示设计原则、开发流程及性能优化的关键步骤。文章不仅提供理论指导,还附带代码示例,旨在帮助开发者构建更优的后端服务。
|
5月前
|
存储 设计模式 API
深入浅出后端开发:从零到一构建RESTful API
【8月更文挑战第29天】本文旨在引导读者理解后端开发的精髓,并通过实际代码示例,展示如何从无到有构建一个RESTful API。文章首先解释后端开发的基本概念,然后逐步深入到API设计的原则和最佳实践,最后通过一个具体的代码示例,让读者能够动手实践,体验后端开发的乐趣。
|
5月前
|
缓存 API 数据库
构建高效后端API的五大秘诀
【8月更文挑战第27天】在数字化时代,一个快速、可靠且易于维护的后端API是成功的关键。本文将深入探讨如何构建高效的后端API,涵盖设计原则、工具选择、性能优化、安全性强化以及文档编写等方面。通过实际代码示例和最佳实践,我们将揭示如何打造一个既灵活又强大的后端系统,满足现代应用的需求。无论你是初学者还是有经验的开发者,这篇文章都将为你提供宝贵的见解和实用的技巧。
|
5月前
|
缓存 API 数据安全/隐私保护
构建高效后端API:实践与技巧
【8月更文挑战第3天】在数字化时代的浪潮中,后端API的设计和实现成为了软件开发的重要组成部分。本文深入探讨了如何构建一个高效的后端API,不仅涉及技术层面的实践,还包含了设计哲学和最佳实践的分享。通过具体案例分析,我们将了解如何优化API的性能、提高安全性以及确保良好的用户体验。无论你是初学者还是经验丰富的开发者,这篇文章都将提供有价值的见解和启发。
37 4