概述
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); }
如上述代碼所示,有幾個注意事項:
-
需要定義接口
-
繼承自IWxApiWithAccessTokenFilter接口將自動在接口請求啟用AccessTokenApiFilter篩選器,即會自動會在接口請求時帶上access_token。
-
HttpHost用於定義接口跟地址
-
HttpPost用於設置接口請求方法,常用特性有:
特性名稱 功能描述 備注 HttpHostAttribute 請求服務http絕對完整主機域名 優先級比Options配置低 HttpGetAttribute 聲明Get請求方法與路徑 支持null、絕對或相對路徑 HttpPostAttribute 聲明Post請求方法與路徑 支持null、絕對或相對路徑 HttpPutAttribute 聲明Put請求方法與路徑 支持null、絕對或相對路徑 HttpDeleteAttribute 聲明Delete請求方法與路徑 支持null、絕對或相對路徑 HeaderAttribute 聲明請求頭 常量值 TimeoutAttribute 聲明超時時間 常量值 FormFieldAttribute 聲明Form表單字段與值 常量鍵和值 FormDataTextAttribute 聲明FormData表單字段與值 常量鍵和值
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(); } }
