- Notifications
You must be signed in to change notification settings - Fork 450
WebApiClient高级
过滤器的接口是IApiActionFilterAttribute,WebApiClient提供默认ApiActionFilterAttribute抽象基类,比从IApiActionFilterAttribute实现一个过滤器要简单得多。
这是一个用于调试追踪的过滤器,可以将请求与响应内容写入指定输出目标。如果输出目标是LoggerFactory,需要在HttpApiConfig配置LoggerFactory实例或ServiceProvider实例。
接口或方法使用[TraceFilter]
[TraceFilter(OutputTarget = OutputTarget.Console)] // 输出到控制台窗口 public interface IUserApi : IHttpApi { // GET {url}?account={account}&password={password}&something={something} [HttpGet] [Timeout(10 * 1000)] // 10s超时 Task<string> GetAboutAsync( [Url] string url, UserInfo user, string something); }请求之后输出请求信息
var userApi = HttpApi.Resolve<IUserApi>(); var about = await userApi .GetAboutAsync("webapi/user/about", user, "somevalue"); IUserApi.GetAboutAsync [REQUEST] 2018-10-08 23:55:25.775 GET /webapi/user/about?Account=laojiu&password=123456&BirthDay=2018-01-01&Gender=1&something=somevalue HTTP/1.1 Host: localhost:9999 [RESPONSE] 2018-10-08 23:55:27.047 This is from NetworkSocket.Http [TIMESPAN] 00:00:01.2722715[SignFilter] public interface IUserApi : IHttpApi { ... } class SignFilter : ApiActionFilterAttribute { public override Task OnBeginRequestAsync(ApiActionContext context) { var sign = DateTime.Now.Ticks.ToString(); context.RequestMessage.AddUrlQuery("sign", sign); return base.OnBeginRequestAsync(context); } }当我们需要为每个请求的url额外的动态添加一个叫sign的参数,这个sign可能和配置文件等有关系,而且每次都需要计算,就可以如上设计与应用一个SignFilter。
全局过滤器的执行优先级比非全局过滤器的要高,且影响全部的请求方法,其要求实现IApiActionFilter接口,并实例化添加到HttpApiConfig的GlobalFilters。像[TraceFilter]等一般过滤器,也是实现了IApiActionFilter接口,也可以添加到GlobalFilters作为全局过滤器。
class MyGlobalFilter : IApiActionFilter { public Task OnBeginRequestAsync(ApiActionContext context) { // do something return Task.CompletedTask; } public Task OnEndRequestAsync(ApiActionContext context) { // do something return Task.CompletedTask; } }添加到GlobalFilters
var myFilter = new MyGlobalFilter(); HttpApi.Register<IUserApi>().ConfigureHttpApiConfig(c => { c.GlobalFilters.Add(myFilter); }); /// <summary> /// 表示提供client_credentials方式的token过滤器 /// </summary> public class TokenFilter : AuthTokenFilter { /// <summary> /// 获取提供Token获取的Url节点 /// </summary> public string TokenEndpoint { get; private set; } /// <summary> /// 获取client_id /// </summary> public string ClientId { get; private set; } /// <summary> /// 获取client_secret /// </summary> public string ClientSecret { get; private set; } /// <summary> /// OAuth授权的token过滤器 /// </summary> /// <param name="tokenEndPoint">提供Token获取的Url节点</param> /// <param name="client_id">客户端id</param> /// <param name="client_secret">客户端密码</param> public TokenFilter(string tokenEndPoint, string client_id, string client_secret) { this.TokenEndpoint = tokenEndPoint ?? throw new ArgumentNullException(nameof(tokenEndPoint)); this.ClientId = client_id ?? throw new ArgumentNullException(nameof(client_id)); this.ClientSecret = client_secret ?? throw new ArgumentNullException(nameof(client_secret)); } /// <summary> /// 请求获取token /// 可以使用TokenClient来请求 /// </summary> /// <returns></returns> protected override async Task<TokenResult> RequestTokenResultAsync() { var tokenClient = new TokenClient(this.TokenEndpoint); return await tokenClient.RequestClientCredentialsAsync(this.ClientId, this.ClientSecret); } /// <summary> /// 请求刷新token /// 可以使用TokenClient来刷新 /// </summary> /// <param name="refresh_token">获取token时返回的refresh_token</param> /// <returns></returns> protected override async Task<TokenResult> RequestRefreshTokenAsync(string refresh_token) { var tokenClient = new TokenClient(this.TokenEndpoint); return await tokenClient.RequestRefreshTokenAsync(this.ClientId, this.ClientSecret, refresh_token); } }添加到GlobalFilters
var tokenFilter = new TokenFilter ("http://localhost/tokenEndpoint","client","secret"); HttpApi.Register<IUserApi>().ConfigureHttpApiConfig(c => { c.GlobalFilters.Add(tokenFilter); }); WebApiClient内置很多特性,包含接口级、方法级、参数级的,他们分别是实现了IApiActionAttribute接口、IApiActionFilterAttribute接口、IApiParameterAttribute接口、IApiParameterable接口和IApiReturnAttribute接口的一个或多个接口。
例如服务端要求使用x-www-form-urlencoded提交,由于接口设计不合理,目前要求是提交:fieldX= {X}的json文本&fieldY={Y}的json文本 这里{X}和{Y}都是一个多字段的Model,我们对应的接口是这样设计的:
[HttpHost("/upload")] ITask<bool> UploadAsync( [FormField][AliasAs("fieldX")] string xJson, [FormField][AliasAs("fieldY")] string yJson);显然,我们接口参数为string类型的范围太广,没有约束性,我们希望是这样子
[HttpHost("/upload")] ITask<bool> UploadAsync([FormFieldJson] X fieldX, [FormFieldJson] Y fieldY);[FormFieldJson]将参数值序列化为Json并做为表单的一个字段内容
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] class FormFieldJson: Attribute, IApiParameterAttribute { public async Task BeforeRequestAsync(ApiActionContext context, ApiParameterDescriptor parameter) { var options = context.HttpApiConfig.FormatOptions; var json = context.HttpApiConfig.JsonFormatter.Serialize(parameter.Value, options); var fieldName = parameter.Name; await context.RequestMessage.AddFormFieldAsync(fieldName, json); } }try { var user = await userApi.GetByIdAsync("id001"); ... } catch (HttpStatusFailureException ex) { var error = ex.ReadAsAsync<ErrorModel>(); ... } catch (HttpApiException ex) { ... }try { var user1 = await userApi .GetByIdAsync("id001") .Retry(3, i => TimeSpan.FromSeconds(i)) .WhenCatch<HttpStatusFailureException>(); ... } catch (HttpStatusFailureException ex) { var error = ex.ReadAsAsync<ErrorModel>(); ... } catch (HttpApiException ex) { ... } catch(Exception ex) { ... }在一些场景中,你可能不需要使用async/await异步编程方式,WebApiClient提供了Task对象转换为IObservable对象的扩展,使用方式如下:
var unSubscriber = userApi.GetByIdAsync("id001") .Retry(3, i => TimeSpan.FromSeconds(i)) .WhenCatch<HttpStatusFailureException>(); .ToObservable().Subscribe(result => { ... }, ex => { ... });