库简介


WebApiClient是开源在github上的一个httpClient客户端库,内部基于HttpClient开发,是一个只需要定义c#接口(interface),并打上相关特性,即可异步调用http-api的框架 ,支持.net framework4.5+、netcoreapp2.0和netstandard2.0。本文将详细地讲解如何使用WebApiClient进行http接口的调用,给.net开发者提供一种有别于传统的http接口调用编程方式。

1. 设计一个Get请求接口

1.1 最简单的Get请求

public interface MyWebApi : IDisposable
{
// GET http://www.mywebapi.com/webapi/user?account=laojiu
[HttpGet("http://www.mywebapi.com/webapi/user")]
ITask<string> GetUserByAccountAsync(string account);
} var myWebApi = HttpApiClient.Create<MyWebApi>();
var userStr = await myWebApi.GetUserByAccountAsync("laojiu");
myWebApi.Dispose();

1.2 使用[HttpHost]特性

如果你有多个接口,而且都指向对一服务器,可以将请求的域名抽出来放到HttpHost特性,接口的代码如下:

[HttpHost("http://www.mywebapi.com")]
public interface MyWebApi : IDisposable
{
// GET /webapi/user?account=laojiu
[HttpGet("/webapi/user")]
ITask<string> GetUserByAccountAsync(string account);
}

1.3 使用强类型返回值处理xml或json

如果接口的返回内容是xml或json,你希望将它自动映射为强类型的模型,需要给接口打上对应的[XmlReturn]或[JsonResult]。实际上有一个[AutoReturn],它会根据回复头标识,自动选择不同的转换器转换为TResult类型的结果,默认的,每个接口都使用了[AutoReturn],除非给接口显性地给方法配置了[XmlReturn]或[JsonResult]。如果以上返回的userStr是UserInfo类型的xml或json文本,那么强类型的代码声明如下:

[HttpHost("http://www.mywebapi.com")]
public interface MyWebApi : IDisposable
{
// GET /webapi/user?account=laojiu
[HttpGet("/webapi/user")]
ITask<UserInfo> GetUserByAccountAsync(string account);
} [HttpHost("http://www.mywebapi.com")]
[JsonReturn] // 指明使用Json处理返回值为UserInfo类型
public interface MyWebApi : IDisposable
{
// GET /webapi/user?account=laojiu
[HttpGet("/webapi/user")]
ITask<UserInfo> GetUserByAccountAsync(string account);
}

2.请求URL的多个参数

2.1 参数平铺

你可以将多个参数一一设计为接口的参数,类似于:

// GET /webapi/user?account=laojiu&password=123456
[HttpGet("/webapi/user")]
ITask<UserInfo> GetUserAsync(string account, string password);

2.2 参数合并到模型

也可以将所有参数合到一个简单的多属性模型对象:

public class MyParameters
{
public string Account { get; set; }
public string Password { get; set; }
} // GET /webapi/user?account=laojiu&password=123456
[HttpGet("/webapi/user")]
ITask<UserInfo> GetUserAsync(MyParameters parameters);

2.3 模型+简单参数混合

在一些场景中,除了提交多属性模型对象之外,可能还需要一个简单类型的额外参数,你可以如下编写接口:

// GET /webapi/user?account=laojiu&password=123456&birthDay=2010-01-01 01:01:01
[HttpGet("/webapi/user")]
ITask<UserInfo> GetUserAsync(MyParameters parameters,DateTime birthDay);

上面这里,你可能会遇到一个问题,birthDay会简单的ToString()值做为参数值,如果你希望只需要日期而不包含时间,你可以给birthDay指定格式:

// GET /webapi/user?account=laojiu&password=123456&birthDay=2010-01-01
[HttpGet("/webapi/user")]
ITask<UserInfo> GetUserAsync(
MyParameters parameters,
[PathQuery("yyyy-MM-dd")] DateTime birthDay);

实际上,对于没有任何特性修饰的每个参数,都默认被[PathQuery]修饰,表示做为请求路径或请求参数处理,[PathQuery]的构造器重载方法可以指定日期时间格式。

3.设计一个Post请求接口

3.1使用x-www-form-urlencoded提交请求

// POST webapi/user
// Body Account=laojiu&Password=123456
[HttpPost("/webapi/user")]
ITask<UserInfo> UpdateUserWithFormAsync([FormContent] UserInfo user);

设计风格和Get请求是差不多的,你应该发现,接口参数被[FormContent]修饰了,[FormContent]的作用是将模型参数user以key1=value1&key2=value2的方式写入到请求内容中。如果你还需要提供一个额外的简单类型参数,需要使用[FormField]修饰这个参数,可以这样设计接口:

// POST webapi/user
// Body Account=laojiu&Password=123456&fieldX=xxx
[HttpPost("/webapi/user")]
ITask<UserInfo> UpdateUserWithFormAsync(
[FormContent] UserInfo user,
[FormField] string fieldX);

3.2使用multipart/form-data提交请求

// POST webapi/user
[HttpPost("/webapi/user")]
ITask<UserInfo> UpdateUserWithMulitpartAsync([MulitpartContent] UserInfo user); // POST webapi/user
[HttpPost("/webapi/user")]
ITask<UserInfo> UpdateUserWithMulitpartAsync(
[MulitpartContent] UserInfo user,
[MulitpartText] string nickName,
MulitpartFile file);

需要了解的是,[MulitpartText]表示是一个文本项,而MulitpartFile表示一个文件项,MulitpartFile实现了IApiParameterable接口,它不需要任何特性的修饰,它能提供自我解释和处理。

3.3提交Json或Xml文本

对于json和xml,只能一次性提交一个参数,不支持额外参数之说

// POST webapi/user
// Body user的json文本
[HttpPost("/webapi/user")]
ITask<UserInfo> UpdateUserWithJsonAsync([JsonContent] UserInfo user); // POST webapi/user
// Body user的xml文本
[HttpPost("/webapi/user")]
ITask<UserInfo> UpdateUserWithXmlAsync([XmlContent] UserInfo user);

如果你的UserInfo有DateTime类型的属性,你可以使用[JsonContent("时间格式")]来修饰接口参数,否则时间格式使用HttpApiConfig的DateTimeFormate。

3.4 提交原始的HttpContent

// POST webapi/user
// Body Account=laojiu&Password=123456
[HttpPost("/webapi/user")]
ITask<UserInfo> UpdateUserWithFormAsync(FormUrlEncodedContent user); // POST webapi/user
// Body Account=laojiu&Password=123456&age=18
[HttpPost("/webapi/user")]
ITask<UserInfo> UpdateUserWithFormAsync(
[HttpContent] FormUrlEncodedContent user,
[FormField] int age);

默认的,所有System.Net.Http.HttpContent类型的参数,都会被[HttpContent]特性修饰,而且可以与表单字段特性等混合使用。值得说明的话,传统的System.Net.Http.HttpContent类型参数必须放到其它表单字段参数的前面。

4. 动态指定请求的域名或Url

4.1 域名动态而相对路径固定

以上的例子,请求的根路径都是硬编码,而在不少场景中是放在配置文件中的,可以在创建接口实例时创建配置项:

var config = new HttpApiConfig
{
// 请求的域名,会覆盖[HttpHost]特性
HttpHost = new Uri("http://www.webapiclient.com"),
};
var myWebApi = HttpApiClient.Create<MyWebApi>(config);
var userStr = await myWebApi.GetUserByAccountAsync("laojiu");
myWebApi.Dispose();

4.2 每个请求接口的URL路径都是动态的

有时,多个接口方法的全部URL都是运行时才确定的,这时,需要给每个接口做如下的调整,注意[Url]特性表示参数是请求的URL,要求必须放在第一个参数。:

public interface MyWebApi : IDisposable
{
// GET {URL}?account=laojiu
[HttpGet]
ITask<string> GetUserByAccountAsync([Url] string url, string account);
}

4.3 相对路径某个分段动态

有时,有些接口会将某个参数做路径的一个分段,比如GET http://www.webapiclient.com/{account},这里的{account}是动态的,获取哪个账号的资料就填写哪个账号,可以如下设计接口

public interface MyWebApi : IDisposable
{
// GET http://www.webapiclient.com/laojiu
[HttpGet("http://www.webapiclient.com/{account}"]
ITask<string> GetUserByAccountAsync(string account);
}

5.参数别名或属性别名

5.1 参数别名

有些服务端接口要求的键名与你的编程风格不一致,或者使用了特殊的键名为.net语言不允许的参数名,你可以使用[AliasAs("name")]来给参数或模型的属性别名。

public interface MyWebApi : IDisposable
{
// GET http://www.mywebapi.com/webapi/user?_name=laojiu
[HttpGet("http://www.mywebapi.com/webapi/user")]
ITask<string> GetUserByAccountAsync(
[AliasAs("_name")] string account);
}

5.2 模型的属性别名

public class UserInfo
{
[AliasAs("loginAccount")]
public string Account { get; set; } public string Password { get; set; }
}

6.特性的范围和优先级

6.1 特性的范围

有些特性比如[Header],可以修饰于接口、方法和参数,使用不同的构造器和修饰于不同的地方产生的含义和结果是有点差别的:

  • 修饰接口时,表示接口下的所有方法在请求前都会添加这个请求头;
  • 修饰方法时,表示此方法在语法前添加这个请求头;
  • 修饰参数时,表示参数的值将做为请求头的值,由调用者动态传入;

6.2 特性的优先级

有些特性比如[AutoReturn]和[JsonReturn],可以修饰于接口和方法,但特性的AllowMultiple为false,如果在接口级生明方法级[AutoReturn],在方法级上声明[JsonReturn],此方法实际生效的是[JsonReturn];再比如[Timeout]特性,如果在接口级声明[Timeout(5000)]在方法级声明[Timeout(10000)],实际生效的是[Timeout(10000)],总结如下:

  • AllowMultiple为false的同一个特性,方法级比接口级优先级高
  • AllowMultiple为false的不同类型的[ReturnAttribute],方法级比接口级优先级高

.net的retrofit--WebApiClient库的更多相关文章

  1. Rxjava, RxAndroid, Retrofit 等库的使用

    RxJava的基本用法: 关于 unSubscribe() 的调用问题: There is no need to unsubscribe in onCompleted. Take a look at  ...

  2. WebApiClient库支持AOT

    1 库简介 WebApiClient是开源在github上的一个http客户端库,内部基于HttpClient开发,只需要定义c#接口(interface),并打上相关特性,即可异步调用http-ap ...

  3. 一种优雅的条件引用第三方.net库的方法

    1.遇到的问题 今年我一直在开发一个WebApiClient库,旨在.net下能像java的retrofit一样,方便地请求服务端的http接口.在这restful api盛行的年代,json的身影无 ...

  4. WebApiClient的JsonPatch局部更新

    1. 文章目的 随着WebApiClient的不断完善,越来越多开发者选择WebApiClient替换原生的HttpClient,本文将介绍使用WebApiClient来完成JsonPatch提交的新 ...

  5. WebApiClient的接口输入验证

    1. 文章目的 随着WebApiClient的不断完善,越来越多开发者选择WebApiClient替换原生的HttpClient,本文将介绍WebApiClient的接口参数输入有效性验证的新特性. ...

  6. WebApiClient百度地图服务接口实践

    1. 文章目的 随着WebApiClient的不断完善,越来越多开发者选择WebApiClient替换原生的HttpClient,然而在应用到实际项目中多多少少会遇到一些项目结合上的疑问和困难,本文将 ...

  7. RxJava+Retrofit+OkHttp,一步一步封装网络框架;

    使用RxJava+Retrofit+OkHttp,首先在build.gradle添加: compile 'com.squareup.okhttp3:okhttp:3.8.1' compile 'com ...

  8. Android RxJava/RxAndroid结合Retrofit使用

    概述 RxJava是一个在 Java VM 上使用可观測的序列来组成异步的.基于事件的程序的库.更重要的是:使用RxJava在代码逻辑上会非常简洁明了,尤其是在复杂的逻辑上.告别迷之缩进. RxAnd ...

  9. .net的retrofit--WebApiClient底层篇

    前言 本篇文章的内容是WebApiClient底层说明,也是WebApiClient系列接近尾声的一篇文章,如果你没有阅读过之前的的相关文章,可能会觉得本文章的内容断层,WebApiClient系列文 ...

  10. 请给你的短信验证码接口加上SSL双向验证

    序言 去年年底闲来几天,有位同事专门在网上找一些注册型的app和网站,研究其短信接口是否安全,半天下来找到30来家,一些短信接口由于分析难度原因,没有继续深入,但差不多挖掘到20来个,可以肆意被调用, ...

随机推荐

  1. Lucene41PostingWriter源代码分析

    原来看lucene4.0的posting格式(http://blog.csdn.net/jollyjumper/article/details/30017581),发现这还是比較简单的VInt格式,据 ...

  2. 百度地图点集文档使用python的re模块处理成json的相关写法

    这个实在不好起名字.写这个还不是因为被渣度坑的不要不要的.为什么说他坑呢.参考一下这两个截图的txt文档: 文档资源下载地址:  http://lbsyun.baidu.com/index.php?t ...

  3. 小白的Python之路 day2 列表、元组操作

    1. 列表.元组操作 列表是我们最以后最常用的数据类型之一,通过列表可以对数据实现最方便的存储.修改等操作 定义列表 1 names = ['Tom','Jack','Qian'] 通过下标访问列表中 ...

  4. LR接口压力测试实战(限量抢红包接口)

    一.业务描述:微信群中,运营人员放出活动链接,用户单击活动链接进入活动页面如下图,用户输入手机号抢红包(红包数量有限) 二.接口测试代码 Action() { lr_rendezvous(" ...

  5. jQuery: $.extend()用法总结

    1.重载原型 $.extend({},src1,src2,src3...) Jquery的扩展方法extend是我们在写插件的过程中常用的方法,该方法有一些重载原型. 它的含义是将src1,src2, ...

  6. [数据结构]C语言链表实现

    我学数据结构的时候也是感觉很困难,当我学完后我发现了之所以困难时因为我没有系统的进行学习,而且很多教授都只是注重数据结构思想,而忽略了代码方面,为此我写了这些博文给那些试图自学数据结构的朋友,希望你们 ...

  7. 贪心算法——Fence Repair(POJ 3253)

    题目描述 农夫约翰为了修理栅栏,要将一块很长的木板切割成N块.准备切成的木板长度为L1,L2,L3--LN,未切割前木板的长度恰好为切割后木板长度的总和.每次切断木板时,需要的开销为这块木板的长度.请 ...

  8. treeview插件使用:根据子节点选中父节点

    鄙人公司没有专门的前端,所以项目开发中都是前后端一起抡.最近用bootstrap用的比较频繁,发现bootstrap除了框架本身的样式组件外,还提供了多种插件供开发者选择.本篇博文讲的就是bootst ...

  9. jsDOM编程-拖拽层

    页面样式代码: <!doctype html><html><head><meta http-equiv="content-type" co ...

  10. [知了堂学习笔记]_用JS制作《飞机大作战》游戏_第4讲(创建敌方飞机、敌方飞机发射子弹、玩家子弹击中敌方小飞机,小飞机死亡)

    一.创建敌方飞机 1.思考创建思路: 创建敌方飞机思路与创建玩家飞机思路一样: (1)思考敌方飞机具备什么属性: 敌方飞机的图片.坐标.飞行速度.状态(是否被击中) 设置小飞机被击中时消失时间.飞机可 ...