.NET Core 使用 WebApiClient.JIT 调用第三方接口

WebApiClient.JIT :一个基于HttpClient的开源项目。您只需要定义c#接口并修改相关功能即可异步调用远程http接口的客户端库。

nuget上搜索WebApiClient.JIT,net和core都支持

作者的事例类

using Demo.HttpClients;
using Demo.HttpServices;
using System;
using System.Threading;
using System.Threading.Tasks;
using WebApiClient;
using WebApiClient.Parameterables;

namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            Init();
            RequestAsync().Wait();
            Console.ReadLine();
        }

        /// <summary>
        /// 初始化配置
        /// </summary>
        static void Init()
        {
            HttpServer.Start(9999);

            //  注册IUserApi 配置其工厂
            HttpApi.Register<IUserApi>().ConfigureHttpApiConfig(c =>
            {
                c.HttpHost = new Uri("http://localhost:9999/");
                c.FormatOptions.DateTimeFormat = DateTimeFormats.ISO8601_WithMillisecond;
            });
        }

        /// <summary>
        /// 请求接口
        /// </summary>
        /// <returns></returns>
        private static async Task RequestAsync()
        {
            var userApi = HttpApi.Resolve<IUserApi>();

            var user = new UserInfo
            {
                Account = "laojiu",
                Password = "123456",
                BirthDay = DateTime.Parse("2018-01-01 12:30:30"),
                Email = "laojiu@webapiclient.com",
                Gender = Gender.Male
            };

            var about = await userApi
                .GetAboutAsync("webapi/user/about", user, "somevalue");

            var aboutCache = await userApi
                .GetAboutAsync("webapi/user/about", user, "somevalue");

            var user1 = await userApi
                .GetByIdAsync("id001", CancellationToken.None);

            var user2 = await userApi
                .GetByAccountAsync("laojiu", CancellationToken.None);

            var user3 = await userApi
                .UpdateWithFormAsync(user, nickName: "老九", age: 18)
                .Retry(3, i => TimeSpan.FromSeconds(i))
                .WhenCatch<HttpStatusFailureException>();

            var user4 = await userApi
                .UpdateWithJsonAsync(user);

            var user5 = await userApi
                .UpdateWithXmlAsync(user).HandleAsDefaultWhenException();

            var file = new MulitpartFile("file.data");
            file.UploadProgressChanged += (s, e) =>
            {
                // Console.WriteLine(e.Progress);
            };

            var user6 = await userApi
                .UpdateWithMulitpartAsync(user, "老九", 18, file);

        }
    }
}
using System.ComponentModel.DataAnnotations;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using WebApiClient;
using WebApiClient.Attributes;
using WebApiClient.Parameterables;

namespace Demo.HttpClients
{
    /// <summary>
    /// 用户操作接口
    /// </summary>
    [TraceFilter(OutputTarget = OutputTarget.Console | OutputTarget.Debug)]
    [HttpHost("http://localhost:9999/")] // HttpHost可以在Config传入覆盖
    public interface IUserApi : IHttpApi
    {
        // GET {url}?account={account}&password={password}&something={something}
        [HttpGet]
        [Cache(10 * 1000)]
        Task<string> GetAboutAsync(
            [Uri] string url,
            UserInfo user,
            string something);

        // /GET webapi/user/GetById?id=id001
        // Return HttpResponseMessage
        [HttpGet("webapi/user/GetById/{id}")]
        [BasicAuth("userName", "password")]
        [Timeout(10 * 1000)] // 10s超时
        ITask<HttpResponseMessage> GetByIdAsync(
            [Required]string id,
            CancellationToken token);

        // GET webapi/user/GetByAccount?account=laojiu
        // Return 原始string内容
        [HttpGet("webapi/user/GetByAccount")]
        ITask<string> GetByAccountAsync(
            [Required]string account,
            CancellationToken token);


        // POST webapi/user/UpdateWithForm  
        // Body Account=laojiu&Password=123456&name=value&nickName=老九&age=18
        // Return json或xml内容
        [HttpPost("webapi/user/UpdateWithForm")]
        [FormField("name", "value")] // 固定的参数值可以这么写
        ITask<UserInfo> UpdateWithFormAsync(
            [FormContent, Required] UserInfo user,
            [FormField] string nickName,
            [FormField, Range(1, 100)] int? age);

        // POST webapi/user/UpdateWithJson
        // Body {"Account":"laojiu","Password":"123456"}
        // Return json或xml内容
        [HttpPost("webapi/user/UpdateWithJson")]
        ITask<UserInfo> UpdateWithJsonAsync(
            [JsonContent, Required] UserInfo user);

        // POST webapi/user/UpdateWithXml 
        // Body <?xml version="1.0" encoding="utf-8"?><UserInfo><Account>laojiu</Account><Password>123456</Password></UserInfo>
        // Return xml内容       
        [HttpPost("webapi/user/UpdateWithXml")]
        ITask<UserInfo> UpdateWithXmlAsync(
            [XmlContent, Required] UserInfo user);

        // POST webapi/user/UpdateWithMulitpart  
        // Body multipart/form-data
        // Return json或xml内容
        [HttpPost("webapi/user/UpdateWithMulitpart")]
        ITask<UserInfo> UpdateWithMulitpartAsync(
            [MulitpartContent, Required] UserInfo user,
            [MulitpartText] string nickName,
            [MulitpartText] int age,
            MulitpartFile file);

        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="uri">文件相对或绝对路径</param>
        /// <returns></returns>
        [HttpGet]
        [TraceFilter(TraceResponse = false)]
        ITask<HttpResponseFile> DownloadFileAsync([Uri, Required] string uri);
    }
}

自己做的测试

using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Script.Serialization;
using WebApiClient;

namespace ConsoleApp1
{

    public class Program
    {
        static void Main(string[] args)
        {
            Init();
            RequestAsync().Wait();
            Console.ReadLine();
        }
        /// <summary>
        /// 初始化配置
        /// </summary>
        static void Init()
        {
            HttpServer.Start(9999);

            //  注册IUserApi 配置其工厂
            HttpApi.Register<IUserApi>().ConfigureHttpApiConfig(c =>
            {
                c.HttpHost = new Uri("http://localhost:42926/");
                c.FormatOptions.DateTimeFormat = DateTimeFormats.ISO8601_WithMillisecond;
                c.GlobalFilters.Add(new ApiTokenFilter(""));//添加Hearder
            });
        }
        /// <summary>
        /// 请求接口
        /// </summary>
        /// <returns></returns>
        private static async Task RequestAsync()
        {
            var userApi = HttpApi.Resolve<IUserApi>();
            string value = "{\"name\":\"1\",\"id\":\"2\"}";
            var about = await userApi
               .GetAboutAsync("api/Test/GetValue", value);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WebApiClient;
using WebApiClient.Attributes;

namespace ConsoleApp1
{
    [TraceFilter(OutputTarget = OutputTarget.Console | OutputTarget.Debug)]
    [HttpHost("http://localhost:9999/")] // HttpHost可以在Config传入覆盖
    public interface IUserApi : IHttpApi
    {
        [HttpGet]
        [Cache(10 * 1000)]
        //[Header("Authorization", "Basic 11111")]
        Task<string> GetAboutAsync(
           [Uri] string url,
           string value);
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WebApiClient;
using WebApiClient.Contexts;

namespace ConsoleApp1
{
    class ApiTokenFilter : IApiActionFilter
    {
        private  string token { get; set; }
        public ApiTokenFilter(string token) 
        {
            this.token = token;
        }          
        public Task OnBeginRequestAsync(ApiActionContext context)
        {
            context.RequestMessage.Headers.Add("Authorization", $"Basic {token}");

            return Task.FromResult<object>(null);
        }

        public Task OnEndRequestAsync(ApiActionContext context)
        {
            return Task.FromResult<object>(null);
        }
    }
}

.NET Core 使用 WebApiClient.JIT 调用第三方接口

开始前首先通过 NuGet 引入包,当前使用版本为 v1.0.9,发布日期 2019年5月21日
在这里插入图片描述
Github:https://github.com/dotnetcore/WebApiClient/tree/WebApiClient.JITAOT 注意底部有Wiki文档对使用非常有帮助,请仔细阅读。

1.新建 xxxHttpsModule 并在 Startup ConfigureServices 中注入。(单独新建 HttpsModule 是为了将不同的业务分离)

public static class xxxHttpsModule
{
    public static IServiceCollection Register(IServiceCollection services, IConfigurationRoot appConfiguration)
    {
        services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
 
        void config(HttpApiConfig c)
        {
            c.HttpHost = new Uri(appConfiguration["Server:Url"]);//AppSettings.json 中的服务器地址
            c.GlobalFilters.Add(new DefaultHeaderAttribute(appConfiguration));
        }
 
        services.AddHttpApi<IxxxApi>().ConfigureHttpApiConfig(config);//注入接口
 
        return services;
    }
}

AddHttpApi 是一个扩展方法,这样写只是为了在多次使用时,看起来更协调。

/// <summary>
/// 基于DependencyInjection的扩展
/// </summary>
public static class DependencyInjectionExtensions
{
    /// <summary>
    /// 添加HttpApi
    /// 返回HttpApi工厂
    /// </summary>
    /// <typeparam name="TInterface">接口类型</typeparam>
    /// <param name="services"></param>
    /// <returns></returns>
    public static HttpApiFactoryBuilder<TInterface> AddHttpApi<TInterface>(this IServiceCollection services)
        where TInterface : class, IHttpApi
    {
        return new HttpApiFactoryBuilder<TInterface>(services);
    }
}

IxxxApi 接口中的内容就是第三方接口的内容

[TraceFilter(OutputTarget = OutputTarget.Console)]//这个设置可以将日志输入到控制台
public interface IxxxApi : IHttpApi
{
    /// <summary>
    /// 创建xxx信息
    /// </summary>
    /// <param name="input">实体</param>
    /// <returns></returns>
    [Timeout(10000)]
    [HttpPost("/api/v2/x/xxx")]
    ITask<AjaxResponse<List<xxxApiDto>>> GetxxxPagedList([JsonContent] xxxDto input);
 
    /// <summary>
    /// 获取xxx信息
    /// </summary>
    /// <param name="pageIndex">页码</param>
    /// <returns></returns>
    [Timeout(10000)]
    [HttpGet("/api/v2/x/xxx")]
    ITask<AjaxResponse<List<xxxApiDto>>> GetxxxPagedList(int pageIndex);
}

2.创建 HttpApi 实例工厂创建器类,具体内容如下。目前没有修改过里面的内容。

using System;
using WebApiClient;
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
 
/// <summary>
/// HttpApi实例工厂创建器
/// </summary>
/// <typeparam name="TInterface"></typeparam>
public class HttpApiFactoryBuilder<TInterface> where TInterface : class, IHttpApi
{
    private bool keepCookieContainer = true;
 
    private TimeSpan lifeTime = TimeSpan.FromMinutes(2d);
 
    private TimeSpan cleanupInterval = TimeSpan.FromSeconds(10d);
 
    private Action<HttpApiConfig, IServiceProvider> configOptions;
 
    private Func<IServiceProvider, HttpMessageHandler> handlerFactory;
 
 
    /// <summary>
    /// HttpApi实例工厂创建器
    /// </summary>
    /// <param name="services"></param>
    public HttpApiFactoryBuilder(IServiceCollection services)
    {
        services.AddSingleton<IHttpApiFactory<TInterface>, HttpApiFactory<TInterface>>(p =>
        {
            return new HttpApiFactory<TInterface>()
                .SetLifetime(this.lifeTime)
                .SetCleanupInterval(this.cleanupInterval)
                .SetKeepCookieContainer(this.keepCookieContainer)
                .ConfigureHttpMessageHandler(() => this.handlerFactory?.Invoke(p));
        });
 
        services.AddTransient(p =>
        {
            var factory = p.GetRequiredService<IHttpApiFactory<TInterface>>();
            factory.ConfigureHttpApiConfig(c =>
            {
                c.ServiceProvider = p;
                this.configOptions?.Invoke(c, p);
            });
            return factory.CreateHttpApi();
        });
    }
 
    /// <summary>
    /// 配置HttpApiConfig
    /// </summary>
    /// <param name="configOptions">配置选项</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <returns></returns>
    public HttpApiFactoryBuilder<TInterface> ConfigureHttpApiConfig(Action<HttpApiConfig> configOptions)
    {
        if (configOptions == null)
        {
            throw new ArgumentNullException(nameof(configOptions));
        }
        return this.ConfigureHttpApiConfig((c, p) => configOptions.Invoke(c));
    }
 
 
    /// <summary>
    /// 配置HttpApiConfig
    /// </summary>
    /// <param name="configOptions">配置选项</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <returns></returns>
    public HttpApiFactoryBuilder<TInterface> ConfigureHttpApiConfig(Action<HttpApiConfig, IServiceProvider> configOptions)
    {
        this.configOptions = configOptions ?? throw new ArgumentNullException(nameof(configOptions));
        return this;
    }
 
    /// <summary>
    /// 配置HttpMessageHandler的创建
    /// </summary>
    /// <param name="handlerFactory">创建委托</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <returns></returns>
    public HttpApiFactoryBuilder<TInterface> ConfigureHttpMessageHandler(Func<HttpMessageHandler> handlerFactory)
    {
        if (handlerFactory == null)
        {
            throw new ArgumentNullException(nameof(handlerFactory));
        }
        return this.ConfigureHttpMessageHandler(p => handlerFactory.Invoke());
    }
 
    /// <summary>
    /// 配置HttpMessageHandler的创建
    /// </summary>
    /// <param name="handlerFactory">创建委托</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <returns></returns>
    public HttpApiFactoryBuilder<TInterface> ConfigureHttpMessageHandler(Func<IServiceProvider, HttpMessageHandler> handlerFactory)
    {
        this.handlerFactory = handlerFactory ?? throw new ArgumentNullException(nameof(handlerFactory));
        return this;
    }
 
    /// <summary>
    /// 置HttpApi实例的生命周期
    /// </summary>
    /// <param name="lifeTime">生命周期</param>
    /// <exception cref="ArgumentOutOfRangeException"></exception>
    /// <returns></returns>
    public HttpApiFactoryBuilder<TInterface> SetLifetime(TimeSpan lifeTime)
    {
        if (lifeTime <= TimeSpan.Zero)
        {
            throw new ArgumentOutOfRangeException(nameof(lifeTime));
        }
        this.lifeTime = lifeTime;
        return this;
    }
 
 
    /// <summary>
    /// 获取或设置清理过期的HttpApi实例的时间间隔
    /// </summary>
    /// <param name="interval">时间间隔</param>
    /// <exception cref="ArgumentOutOfRangeException"></exception>
    /// <returns></returns>
    public HttpApiFactoryBuilder<TInterface> SetCleanupInterval(TimeSpan interval)
    {
        if (interval <= TimeSpan.Zero)
        {
            throw new ArgumentOutOfRangeException(nameof(interval));
        }
        this.cleanupInterval = interval;
        return this;
    }
 
    /// <summary>
    /// 设置是否维护使用一个CookieContainer实例 该实例为首次创建时的CookieContainer
    /// </summary>
    /// <param name="keep">true维护使用一个CookieContainer实例</param>
    /// <returns></returns>
    public HttpApiFactoryBuilder<TInterface> SetKeepCookieContainer(bool keep)
    {
        this.keepCookieContainer = keep;
        return this;
    }
}

3.调用就和我们自己写的接口一样在构造函数中注入使用,如果不告诉你这是一个第三方接口,完全感觉不出来。

4.WebApiClient 还可以通过 AliasAs 属性自定义字段名称

[Serializable]
public class xxxApiDto
{
    /// <summary>
    /// 序列号
    /// </summary>
    [AliasAs("serialNum")]
    public string SerialNumber { get; set; }
}

目前发现如果在 xxxApiDto 类中属性存在类时, AliasAs 则不会被识别,目前的解决方案是使用 [JsonProperty(PropertyName = "serialNum")],再通过 JsonConvert.SerializeObject(xxx) 转换成 JSON 字符串。

5.请求注入 Headers ,可以通过继承 ApiActionFilterAttribute 类,然后重写 OnBeginRequestAsync 实现。第二种方法也可以接口上通过 Header("参数名") 直接使用。

第二种方法适用于动态设置,可以根据不同的使用场景动态传递 Header 值。具体使用方式如下:

/// <summary>
/// 修改密码
/// </summary>
[HttpPost("/identity/api/v1/User/changepassword")]
Task<AjaxSPDResponse<string>> ChangePassword([Header("Authorization")] string authorization, ModifyPasswordInput input);
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值