概述
Magicodes.Wx.Sdk致力于打造最简洁最易于使用的微信Sdk,逐步包括公众号Sdk、小程序Sdk、企业微信Sdk等,以及Abp VNext集成。
本篇将侧重于讲述如何向Magicodes.Wx.Sdk进行贡献。
WebApiClientCore
Magicodes.Wx.Sdk之简洁很大层面依托于NCC的开源库WebApiClientCore。Magicodes.Wx.Sdk依托WebApiClientCore完成了微信接口的包装和校验。
开源库地址:
https://github.com/dotnetcore/WebApiClient
快速开始
这里我们以【客服消息】【添加客服账号】为例进行讲解,官方接口文档地址为:https://developers.weixin.qq.com/doc/offiaccount/Customer_Service/Customer_Service_Management.html#2。
比如添加客服账号接口官方接口文档说明如下所示:
主体步骤如下:
1)添加接口IKfAccountApi
参考代码如下所示:
/// <summary>
/// 客服管理
/// </summary>
[HttpHost("https://api.weixin.qq.com/customservice/kfaccount/")]
public interface IKfAccountApi : IWxApiWithAccessTokenFilter
{
/// <summary>
/// 添加客服账号
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("add")]
Task<ApiResultBase> AddAsync(AddOrUpdateKfAccountInput input);
/// <summary>
/// 设置客服信息
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("update")]
Task<ApiResultBase> UpdateAsync(AddOrUpdateKfAccountInput input);
/// <summary>
/// 删除客服账号
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost("del")]
Task<ApiResultBase> DelAsync(DelKfAccountInput input);
}
如上述代码所示,有几个注意事项:
2)添加Dto
这一步是非必要的,需要看参数的具体内容和要求。添加客服接口的输入参数代码参考如下:
public class AddOrUpdateKfAccountInput
{
/// <summary>
/// 完整客服帐号,格式为:帐号前缀@公众号微信号,帐号前缀最多10个字符,必须是英文、数字字符或者下划线,后缀为公众号微信号,长度不超过30个字符
/// </summary>
[JsonProperty("kf_account")]
[StringLength(30, MinimumLength = 3)]
[Required]
public string Account { get; set; }
/// <summary>
/// 客服昵称,最长16个字
/// </summary>
[JsonProperty("nickname")]
[StringLength(16, MinimumLength = 1)]
public string Nickname { get; set; }
}
Dto实体的添加这里给大家分享一个小技巧。当实体字段以及层级比较多时,大家可以使用VS的【编辑】==》【选择性粘贴】==》【将Json粘贴为类】:
3)添加ApiResultBase
框架中封装了默认的返回结果基类,如果没有其他额外的返回内容,仅需返回ApiResultBase
即可。如果有额外的范围内容,则需要定义子类继承自ApiResultBase
,在可能的情况下,有可能需要重写部分方法(比如IsSuccess
)。如下述代码:
public class TokenApiResult : ApiResultBase
{
/// <summary>
/// 获取到的凭证
/// </summary>
[JsonProperty("access_token")]
public string AccessToken { get; set; }
/// <summary>
/// 凭证有效时间,单位:秒
/// </summary>
[JsonProperty("expires_in")]
internal int Expires { get; set; }
/// <summary>
/// 凭证过期时间
/// </summary>
public DateTime ExpiresTime { get; set; }
}
至此,一个接口就编写完成了。只需要定义interface和Dto就可以了。是不是非常简单?
4)单元测试编写
/// <summary>
/// 模板消息单元测试
/// </summary>
public class TemplateApiTest : TestBase, IClassFixture<TestWebApplicationFactory>
{
private readonly ITemplateApi templateApi;
public TemplateApiTest(TestWebApplicationFactory webApplicationFactory, ITestOutputHelper output) : base(webApplicationFactory, output)
{
//通过父类的GetRequiredService获取到Api
templateApi = GetRequiredService<ITemplateApi>();
}
/// <summary>
/// 模板消息发送测试
/// </summary>
/// <returns></returns>
[Fact]
public async Task SendAsync_Test()
{
var result = await templateApi.SendAsync(new SendTemplateMessageInput()
{
To = "oXELNwzZgamuLS0JrJhVgdelzKyw",
TemplateId = "riid7aad8OKRQD9Ey6dclWBBkrqZSFDhlpKh0_spGLA",
Data = new System.Collections.Generic.Dictionary<string, TemplateDataItem>()
{
{"first",new TemplateDataItem("测试") },
{"keyword1",new TemplateDataItem("雪雁") },
{"keyword2",new TemplateDataItem("2021.2.5") },
{"remark",new TemplateDataItem("备注") },
}
});
//判断Api是否调用成功,未成功将抛出异常WxSdkException
result.EnsureSuccess();
}
}
整体参考:
https://github.com/xin-lai/Magicodes.Wx.Sdk/pull/1/commits/85263ed9a807581f7315a90fe6e00c51c994d386
其他须知内容
IWxApiWithAccessTokenFilter接口
IWxApiWithAccessTokenFilter
接口用于定义需要携带公众号Acces sToken的接口。
如下述参考代码所示,其启用了AccessTokenApiFilter
筛选器,以及关闭了AcceptContentType的匹配约束(公众号的接口官方写的一塌糊涂,很不规范)。
参考代码:
/// <summary>
///
/// </summary>
[JsonNetReturn(EnsureMatchAcceptContentType = false)]
[AccessTokenApiFilter]
//[LoggingFilter]
public interface IWxApiWithAccessTokenFilter
{
}
AccessTokenApiFilter筛选器
AccessTokenApiFilter
接口筛选器会在启用的API、Action上自动添加access_token参数值。
参考代码如下所示:
public class AccessTokenApiFilter : ApiFilterAttribute
{
public override async Task OnRequestAsync(ApiRequestContext context)
{
ITokenManager tokenManager = context.HttpContext.ServiceProvider.GetRequiredService<ITokenManager>();
string accessToken = await tokenManager.GetAccessTokenAsync();
context.HttpContext.RequestMessage.AddUrlQuery("access_token", accessToken);
}
public override Task OnResponseAsync(ApiResponseContext context)
{
return Task.CompletedTask;
}
}
关于ApiResultBase
ApiResultBase定义了公众号API返回基类,用于接收错误码、错误消息、是否执行成功的判断以及获取友好消息提示。
参考代码:
/// <summary>
/// API请求结果
/// {"errcode":40164,"errmsg":"invalid ip 218.76.8.29 ipv6 ::ffff:218.76.8.29, not in whitelist rid: 60122c35-705c3134-51b45a3d"}
/// </summary>
public class ApiResultBase
{
/// <summary>
/// 返回码
/// </summary>
[JsonProperty("errcode")]
public virtual ReturnCodes ReturnCode { get; set; }
/// <summary>
/// 错误消息
/// </summary>
[JsonProperty("errmsg")]
public virtual string Message { get; set; }
/// <summary>
/// 是否为成功返回
/// </summary>
/// <returns></returns>
public virtual bool IsSuccess()
{
return ReturnCode == ReturnCodes.请求成功;
}
/// <summary>
/// 获取友好提示
/// </summary>
/// <returns></returns>
public virtual string GetFriendlyMessage()
{
return ReturnCode.ToString();
}
}
01
文档官网
https://docs.xin-lai.com/
02
博客
http://www.cnblogs.com/codelove/
03
其他开源库