用于.NET Core,Xamarin和.NET的自动类型安全的REST库,Refit是一个受Square Square Retrofit库影响的库,但它比REST API更容易:
public interface IGitHubApi { [Get("/users/{user}")] Task<User> GetUser(string user); }RestService类生成一个使用HttpClient进行调用的IGitHubApi实现:
var gitHubApi = RestService.For<IGitHubApi>("https://api.github.com"); var octocat = await gitHubApi.GetUser("octocat");对于.NET Core支持,您必须使用csproj类型的项目托管您的Refit接口。 这是因为xproj无法执行不包含在项目文件中的编译时代码生成。 如果您使用xproj作为网站,类库或应用程序,您仍然可以通过创建一个netstandard csproj然后使用从xproj到csproj的项目到项目的引用来使用Refit。 一旦出现“VS 15”和最终的.NET Core工具,此解决方法就不需要了。
每个方法都必须有一个HTTP属性,提供请求方法和相对URL。 有五个内置注释:Get,Post,Put,Delete和Head。 资源的相对URL在注释中指定。[Get("/users/list")]
你可以在URL中指定查询参数:[Get("/users/list?sort=desc")]
可以使用方法上的替换块和参数动态更新请求URL。 替换块是由{}包围的字母数字字符串。如果您的参数名称与URL路径中的名称不匹配,请使用AliasAs属性。
[Get("/group/{id}/users")] Task<List<User>> GroupList([AliasAs("id")] int groupId) 未指定为URL替换的参数将自动用作查询参数。 这与Retrofit不同,其中必须明确指定所有参数。参数名称和URL参数之间的比较不区分大小写,因此如果您在路径/ group/{groupid} /show中命名参数"groupId",它将正常工作。 [Get("/group/{id}/users")] Task<List<User>> GroupList([AliasAs("id")] int groupId, [AliasAs("sort")] string sortOrder); GroupList(4, "desc"); >>> "/group/4/users?sort=desc" PS:这里就是说如果你在GroupList的签名里面使用[AliasAs("sort")] 变量类型 变量名的方式,即使原本的路径里面没有“sort"的属性,也会自动加上去。您可以为应用Headers属性的方法设置一个或多个静态请求标头:
[Headers("User-Agent: Awesome Octocat App")] [Get("/users/{user}")] Task<User> GetUser(string user);也可以通过将Headers属性应用于接口,将静态标头添加到API中的每个请求中:
[Headers("User-Agent: Awesome Octocat App")] public interface IGitHubApi { [Get("/users/{user}")] Task<User> GetUser(string user); [Post("/users/new")] Task CreateUser([Body] User user); }如果头文件的内容需要在运行时设置,则可以通过将Header属性应用到参数来为请求添加具有动态值的头文件:
[Get("/users/{user}")] Task<User> GetUser(string user, [Header("Authorization")] string authorization); // 将标题“Authorization:token OAUTH-TOKEN”添加到请求中 var user = await GetUser("octocat", "token OAUTH-TOKEN");使用标头的最常见原因是授权。 今天,大多数API使用一些口味的oAuth,访问令牌到期并刷新取得更长寿命的令牌。封装这些令牌用法的一种方法是,可以插入一个自定义的HttpClientHandler。举个例子:
class AuthenticatedHttpClientHandler : HttpClientHandler { private readonly Func<Task<string>> getToken; public AuthenticatedHttpClientHandler(Func<Task<string>> getToken) { if (getToken == null) throw new ArgumentNullException("getToken"); this.getToken = getToken; } protected override async Task<HttpResponseMessage> 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。这个类是这样使用的(例如使用ADAL库来管理Xamarin.Auth或任何其他库的自动令牌刷新:
class LoginViewModel { AuthenticationContext context = new AuthenticationContext(...); private async Task<string> GetToken() { // 如果需要,AquireTokenAsync调用将提示用户界面 // 否则默认使用刷新令牌返回一个有效的访问令牌 var token = await context.AcquireTokenAsync("http://my.service.uri/app", "clientId", new Uri("callback://complete")); return token; } public async void LoginAndCallApi() { var api = RestService.For<IMyRestService>(new HttpClient(new AuthenticatedHttpClientHandler(GetToken)) { BaseAddress = new Uri("https://the.end.point/") }); var location = await api.GetLocationOfRebelBase(); } } interface IMyRestService { [Get("/getPublicInfo")] Task<Foobar> SomePublicMethod(); [Get("/secretStuff")] [Headers("Authorization: Bearer")] Task<Location> GetLocationOfRebelBase(); }在上面的例子中,任何时候调用需要身份验证的方法,AuthenticatedHttpClientHandler将尝试获取一个新的访问令牌。 由应用程序提供一个,检查现有访问令牌的到期时间,并在需要时获取新的访问令牌。
不同于Retrofit,其中标题不会相互覆盖,并且都添加到请求中,而不管定义同一个标题的次数如何,Refit对ASP.NET MVC采用与动作过滤器相似的方法采用类似的方法 - 重新定义标题将替换 它按以下顺序排列:
接口上的标题属性(最低优先级)标题属性在方法方法参数的标题属性(最高优先级) [Headers("X-Emoji: :rocket:")] public interface IGitHubApi { [Get("/users/list")] Task<List> GetUsers(); [Get("/users/{user}")] [Headers("X-Emoji: :smile_cat:")] Task<User> GetUser(string user); [Post("/users/new")] [Headers("X-Emoji: :metal:")] Task CreateUser([Body] User user, [Header("X-Emoji")] string emoji); } // X-Emoji: :rocket: var users = await GetUsers(); // X-Emoji: :smile_cat: var user = await GetUser("octocat"); // X-Emoji: :trollface: await CreateUser(user, ":trollface:");在界面或方法上定义的标题可以通过重新定义没有值的静态标题(即不使用:<value>)或为动态标题传递null来删除。 空字符串将被包括为空标题。
[Headers("X-Emoji: :rocket:")] public interface IGitHubApi { [Get("/users/list")] [Headers("X-Emoji")] // 删除X-Emoji标题 Task<List> GetUsers(); [Get("/users/{user}")] [Headers("X-Emoji:")] // 将X-Emoji标题重新定义为空 Task<User> GetUser(string user); [Post("/users/new")] Task CreateUser([Body] User user, [Header("X-Emoji")] string emoji); } // 没有X-Emoji标题 var users = await GetUsers(); // X-Emoji: var user = await GetUser("octocat"); // 没有X-Emoji标题 await CreateUser(user, null); // X-Emoji: await CreateUser(user, "");使用Multipart属性装饰的方法将使用多部分内容类型提交。 此时,multipart方法支持以下参数类型:
字符串(参数名称将用作名称和字符串值作为值)字节数组流FileInfo 参数名称将用作多部分数据中字段的名称。 这可以被AliasAs属性覆盖。要指定字节数组(byte []),Stream和FileInfo参数的文件名和内容类型,需要使用包装类。 ByteArrayPart,StreamPart和FileInfoPart。 public interface ISomeApi { [Multipart] [Post("/users/{id}/photo")] Task UploadPhoto(int id, [AliasAs("myPhoto")] StreamPart stream); }要将Stream传递给此方法,请构建如下所示的StreamPart对象:someApiInstance.UploadPhoto(id, new StreamPart(myPhotoStream, "photo.jpg", "image/jpeg")); 注意:此部分以前描述的AttachmentName属性已被弃用,不推荐使用它。
请注意,在Refit中,与Retrofit不同,没有同步网络请求的选项 - 所有请求都必须通过任务或通过IObservable进行异步。 不像Retrofit,只能通过回调参数创建异步,因为我们生活在async/await未来。
[Post("/users/new")] Task CreateUser([Body] User user); // 如果网络呼叫失败,则会发生这种情况 await CreateUser(someUser);如果type参数是'HttpResponseMessage'或'string',则原始响应消息或作为字符串的内容将分别返回。
// Returns the content as a string (i.e. the JSON data) [Get("/users/{user}")] Task<string> GetUser(string user); //返回原始响应,作为可用于Reactive Extensions的IObservable [Get("/users/{user}")] IObservable<HttpResponseMessage> GetUser(string user);当使用像ASP.NET Web API这样的东西,它是一个相当普遍的模式,拥有一整套CRUD REST服务。 Refit现在允许您使用通用类型定义单个API接口:
public interface IReallyExcitingCrudApi<T, in TKey> where T : class { [Post("")] Task<T> Create([Body] T paylod); [Get("")] Task<List<T>> ReadAll(); [Get("/{key}")] Task<T> ReadOne(TKey key); [Put("/{key}")] Task Update(TKey key, [Body]T payload); [Delete("/{key}")] Task Delete(TKey key); }转载于:https://www.cnblogs.com/mschen/p/10199452.html
相关资源:数据结构—成绩单生成器