跳到主要内容

DotNet实现简单微信SDK

·3 分钟

概述

上一篇文章C#实现微信扫码登录(绕过微信开发平台) - ihuadz 提到封装微信接口,本篇文章将从以下方面介绍如何使用C#实现简单的微信SDK。

仓库地址:https://github.com/ihuadz/QI.WxSdk.git

  • 核心类库
  • 配置
  • 具体实现
  • 如何使用

核心类库介绍

  • WebApiClient

WebApiClient 是一个集高性能高可扩展性于一体的声明式http客户端库,具体使用方法可以查看一下官方文档

其他的都是使用dotnet提供的类库

配置

配置文件长这样,可配置多个小程序

# 微信应用(公众号/小程序)
WeixinSetting:
  # 应用配置数组,可配置多个
  App:
  - AppId: xxxxxxxxxxxxxx
    AppSecret: xxxxxxxxxxxxxxxxxxxxxxx
    Name: xxxxxxxxxxxxxxx
  - AppId: xxxxxxxxxxxxxx
    AppSecret: xxxxxxxxxxxxxxxxxxxxxxx
    Name: xxxxxxxxxxxxxxx

具体实现

封装代码太多,文章内就放出核心代码,全部sdk查看概述

  • IWxApiBase
/// <summary>
/// 微信接口映射API
/// </summary>

[JsonNetReturn(EnsureMatchAcceptContentType = false)]
public interface IWxApiBase
{
}
  • 获取Access Token
// <summary>
    /// 获取全局接口调用凭据, 用于小程序和公众号
    /// </summary>
    [HttpHost("https://api.weixin.qq.com/cgi-bin/")]
    public interface IWxTokenApi : IWxApiBase
    {
        /// <summary>
        /// 获取全局接口调用 Access token
        /// </summary>
        /// <remarks>
        /// 用于小程序和公众号
        /// https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
        /// https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html
        /// </remarks>
        /// <param name="appId">公众号或小程序的应用id</param>
        /// <param name="secret">应用密钥,即appsecret</param>
        /// <param name="grant_type">授权类型,获取access_token填写client_credential</param>
        /// <returns></returns>
        [HttpGet("token")]
        Task<TokenApiResult> GetAsync([Required] string appId, [Required] string secret, string grant_type = "client_credential");

    }
  • 将AccessToken缓存
    /// <summary>
    /// 检查应用配置数据
    /// </summary>
    protected virtual void CheckAppSetting()
    {
        if (CurApp == null) throw new WxException("应用配置不能空");
        if (string.IsNullOrEmpty(CurApp.AppId)) throw new WxException("应用AppId能空");
        if (string.IsNullOrEmpty(CurApp.AppSecret)) throw new WxException("应用AppSecret能空");
    }

    /// <summary>
    /// 获取Access Token
    /// </summary>
    /// <remarks>如果可能,会进行缓存</remarks>
    /// <returns></returns>
    public virtual async Task<string> GetAccessTokenAsync()
    {   
        CheckAppSetting();

        //获取token,先查询缓存
        string token = await GetAccessTokenFromCacheAsync();
        if (!string.IsNullOrEmpty(token)) return token;

        //如果缓存中没有,调用Wx Api获取Token
        IWxTokenApi tokenApi = GetService<IWxTokenApi>();
        TokenApiResult result = await tokenApi.GetAsync(CurApp.AppId, CurApp.AppSecret);

        //缓存token
        await SetAccessTokenToCacheAsync(result.AccessToken, result.Expires);

        return result.AccessToken;
    }
  • 最主要的是这一步,使用WebApiClient过滤器将access_token设置到每次请求微信的url中
/// <summary>
/// WebApiClient过滤器,自动设置url的access_token请求参数
/// </summary>
public class AccessTokenApiFilter : ApiFilterAttribute
{
    /// <summary>
    /// 请求
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public override async Task OnRequestAsync(ApiRequestContext context)
    {
        var tokenManager = context.HttpContext.ServiceProvider.GetRequiredService<ITokenManager>();
        string accessToken = await tokenManager.GetAccessTokenAsync();
        context.HttpContext.RequestMessage.AddUrlQuery("access_token", accessToken);
    }

    /// <summary>
    /// 响应
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public override Task OnResponseAsync(ApiResponseContext context)
    {
        if(context.Result is ApiResultBase apiResult && apiResult!=null)
        {
            //如果请求的accesstoken过期,清除当前缓存的过期token
            if (apiResult.IsAccessTokenInvalid)
            {
                var tokenManager = context.HttpContext.ServiceProvider.GetRequiredService<ITokenManager>();
                tokenManager.ClearAccessTokenAsync();
            }
        }

        return Task.CompletedTask;
    }
}

以上次文章中使用到的请求短链为例,实现请求微信端。

/// <summary>
/// 小程序 URL Link, URL Scheme 接口
/// </summary>
public interface IWxMpUrllinkApi : IWxApiWithAccessTokenFilter
{
    /// <summary>
    /// 获取小程序 URL Link
    /// </summary>
    /// <remarks>
    /// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-link/urllink.generate.html
    /// https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/url-link.html
    /// </remarks>
    /// <param name="input">请求数据</param>
    /// <returns></returns>
    [HttpPost("https://api.weixin.qq.com/wxa/generate_urllink")]
    ITask<GenerateUrlLinkResult> GenerateUrllinkAsync([JsonNetContent] GenerateUrlLinkInput input);
}

IWxApiWithAccessTokenFilter

/// <summary>
/// WebApiClient过滤器,自动设置url的access_token请求参数
/// </summary>
[JsonNetReturn(EnsureMatchAcceptContentType = false)]
[AccessTokenApiFilter]
public interface IWxApiWithAccessTokenFilter
{
}

然后就是WxMpApiService,这里就只放短链相关

/// <summary>
/// 微信小程序接口聚合服务, 生命周期为Scoped
/// </summary>
public class WxMpApiService : WxApiServiceBase
{
    /// <summary>
    /// 小程序 URL Link, URL Scheme 接口
    /// </summary>
    public IWxMpUrllinkApi IUrllinkApi => GetService<IWxMpUrllinkApi>();
}

WxApiServiceBase 接口聚合服务 基类

/// <summary>
/// Wx接口聚合服务 基类
/// </summary>
public class WxApiServiceBase
{
    /// <summary>
    /// 微信接口集合服务
    /// </summary>
    /// <param name="tokenManager"></param>
    public WxApiServiceBase(ITokenManager tokenManager)
    {
        TokenManager = tokenManager;
    }

    /// <summary>
    /// 微信AccessToken管理器
    /// </summary>
    public virtual ITokenManager TokenManager { get; }

    /// <summary>
    /// 得到服务对象
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    protected virtual T GetService<T>()
    {
        return TokenManager.GetService<T>();
    }

    /// <summary>
    /// 设置当前的应用
    /// </summary>
    /// <param name="wxApp"></param>
    public virtual void SetCurApp(WxAppSetting wxApp)
    {
        TokenManager.SetCurApp(wxApp);
    }

    /// <summary>
    /// 设置当前的应用,根据Appid从WxContext中查找
    /// </summary>
    /// <param name="appId"></param>
    public virtual void SetCurApp(string appId)
    {
        TokenManager.SetCurApp(appId);
    }

    /// <summary>
    /// 得到会话当前AppId
    /// </summary>
    /// <returns></returns>
    public virtual string GetCurAppId()
    {
        return TokenManager?.GetCurAppId() ?? string.Empty;
    }

    /// <summary>
    /// 当前应用
    /// </summary>
    /// <returns></returns>
    public virtual WxAppSetting GetCurApp()
    {
        return TokenManager.GetCurApp();
    }
}

至于ITokenManager,自己设计一下就好,都是读取配置和缓存AccessToken的一些操作,也可以查看源码概述

最后就是如何使用了。添加一个扩展类,将封装好的Api注册

/// <summary>
/// 添加WxSdk 接口、服务,IWxSession会话
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddWxSdkAll(this IServiceCollection services)
{
    //注册接口,服务
    services.AddWxSdkApiAndServices();
    //注册token管理器
    services.AddWxSdkTokenManager();

    return services;
}

/// <summary>
/// 注册token管理器,会话服务
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddWxSdkTokenManager(this IServiceCollection services)
{
    //添加WxSession
    services.AddScoped<ITokenManager, TokenManager>();

    return services;
}

/// <summary>
/// 添加WxSdk 接口、服务
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddWxSdkApiAndServices(this IServiceCollection services)
{
    services
        .AddWebApiClient()
        .UseJsonFirstApiActionDescriptor();

    //添加Wx上下文
    services.AddSingleton<WxConfig>();

    //添加微信API映射
    //公众接口
    services.AddHttpApi<IWxTokenApi>();

    //注册聚合/扩展服务 -公众号
    services.AddScoped<WxPaApiService>();

    //小程序接口
    services.AddHttpApi<IWxMpUrllinkApi>();


    //注册聚合/扩展服务 -小程序
    services.AddScoped<WxMpApiService>();

    return services;
}

如何使用

在你的应用中添加注册

//注册微信Sdk集成
services.AddWxSdkAll();

在service里注入后调用

//请求微信短链接口
    var urlResult = await _wxMpService.IUrllinkApi.GenerateUrllinkAsync(new GenerateUrlLinkInput()
    {
        Path = path,
        Query = query,
        EnvironmentVersion = environmentVersion,
        ExpireTimestamp = DateTime.Now.AddMinutes(10).ConvertToTimeStamp()
    });