背景
随着Wcf、Webservice等的SOAP的份额越来越少,以及Restful Api的兴起,目前几乎所有新平台提供的接口,都只提供Restful Api,而.net平台下,没有类似Wcf这么简单的客户端可以直接请求和管理这些Restful api的解决方案,.net平台提供的HttpWebRequest、WebClient和HttpClient这三个类库,可用于实现Http接口的请求,但相比wcf得益于soap自我描述实现的自动生成客户端调用代码,Restful就没这么方便了,无论使用HttpWebRequest还是HttpClient,都需要对每个Api缩写沉长的调用代码。
使用WebApiClient
WebApiClient是在这样的背景下产生一款Http全异步的客户端库,它的出现,大幅度减轻了接口调用者的工作量,而且在调用Http接口上还非常容易维护和更新,还可以轻松应对设计不太友好的一些http接口。
使用WebApiClient,编程人员不再需要手动实现路径拼接、参数拼接、请求体组装和响应映射为模型这些繁琐的过程,以下为WebApiClient应用到项目中的一般流程:
1 声明http接口的Interface
[JsonReturn]
public interface IIotRemotePush : IDisposable
{
/// <summary>
/// 创建远程推送账号
/// </summary>
/// <param name="auth">授权</param>
/// <returns></returns>
[HttpPost("/v1/RemotePush/CreateAccount")]
ITask<ApiResult<PushAccount>> CreateAccountAsync(IotBasicAuth auth);
/// <summary>
/// 获取推送服务信息
/// </summary>
/// <param name="id">pushId</param>
/// <returns></returns>
[HttpGet("/v1/Mqtt/GetPushSevice?id={id}")]
ITask<ApiResult<MqttService>> GetPushSeviceAsync(string id);
}
/// <summary>
/// Api结果接口
/// </summary>
public interface IApiResult
{
/// <summary>
/// 错误码
/// </summary>
ErrorCode Code { get; set; }
/// <summary>
/// 相关提示信息
/// </summary>
string Msg { get; set; }
}
/// <summary>
/// 表示Api结果
/// </summary>
public class ApiResult<T> : IApiResult
{
/// <summary>
/// 错误码
/// </summary>
public ErrorCode Code { get; set; }
/// <summary>
/// 相关提示信息
/// </summary>
public string Msg { get; set; }
/// <summary>
/// 业务数据
/// </summary>
public T Data { get; set; }
}
2 调用http接口
WebApiClient不需要开者实现接口,使用HttpApiClient.Create方法可以动态创建接口的实现类的实例,调用实例的方法,就完成一个Api的请求。
using (var iotApi = HttpApiClient.Create<IIotRemotePush>())
{
var auth = new IotBasicAuth(config.AppId, config.AppToken);
var createResult = await iotApi.CreateAccountAsync(auth);
if (createResult.Code != ErrorCode.NoError)
{
return null;
}
config.PushId = createResult.Data.Id;
config.PushToken = createResult.Data.Token;
await db.SaveChangesAsync();
return config;
}
3 异常定义与异常处理
在以上接口中,接口返回的都是ApiResult
/// <summary>
/// 表示Iot异常
/// </summary>
public class IotException : Exception
{
/// <summary>
/// 错误码
/// </summary>
public ErrorCode ErrorCode { get; private set; }
/// <summary>
/// Iot异常
/// </summary>
/// <param name="apiResult">api结果值</param>
public IotException(IApiResult apiResult)
: base(apiResult.Msg)
{
this.ErrorCode = apiResult.Code;
}
}
我们还应该在Interface上扩展JsonResult,用于将ApiResult的ErrorCode转换为IotException,并抛出:
/// <summary>
/// 表示IotJson结果
/// </summary>
public class IotJsonResultAttribute : JsonReturnAttribute
{
protected override async Task<object> GetTaskResult(ApiActionContext context)
{
var apiResult = await base.GetTaskResult(context) as IApiResult;
if (apiResult != null && apiResult.Code != ErrorCode.NoError)
{
throw new IotException(apiResult);
}
return apiResult;
}
}
然后将新的IotJsonResultAttribute在Interface上替换JsonReturnAttribute:
[IotJsonResult]
public interface IIotRemotePush : IDisposable
{
...
}
最后,调用http接口的时候,可以使用Handle()扩展方法处理异常:
using (var iotApi = HttpApiClient.Create<IIotRemotePush>())
{
var auth = new IotBasicAuth(config.AppId, config.AppToken);
var createResult = await iotApi.CreateAccountAsync(auth)
.Handle()
.WhenCatch<IotException>(ex =>
{
// process exception
return default(ApiResult<PushAccount>);
})
.WhenCatch<Exception>(ex =>
{
// process exception
return default(ApiResult<PushAccount>);
});
if (createResult == null)
{
return null;
}
config.PushId = createResult.Data.Id;
config.PushToken = createResult.Data.Token;
await db.SaveChangesAsync();
return config;
}
WebApiClient现状
WebApiClient项目目前已加入.NET China Foundation,正在为.net开源作出自己的一点贡献。