如何快速編寫一個微信Api?

概述

Magicodes.Wx.Sdk致力於打造最簡潔最易於使用的微信Sdk,逐步包括公衆號Sdk、小程序Sdk、企業微信Sdk等,以及Abp VNext集成。html

本篇將側重於講述如何向Magicodes.Wx.Sdk進行貢獻。git

WebApiClientCore

Magicodes.Wx.Sdk之簡潔很大層面依託於NCC的開源庫WebApiClientCoreMagicodes.Wx.Sdk依託WebApiClientCore完成了微信接口的包裝和校驗。github

開源庫地址:https://github.com/dotnetcore/WebApiClientweb

快速開始

這裏咱們以【客服消息】【添加客服帳號】爲例進行講解,官方接口文檔地址爲:https://developers.weixin.qq.com/doc/offiaccount/Customer_Service/Customer_Service_Management.html#2。小程序

好比添加客服帳號接口官方接口文檔說明以下所示:api

添加客服帳號

主體步驟以下:微信

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);
}

如上述代碼所示,有幾個注意事項:async

  1. 須要定義接口ide

  2. 繼承自IWxApiWithAccessTokenFilter接口將自動在接口請求啓用AccessTokenApiFilter篩選器,即會自動會在接口請求時帶上access_token。

  3. HttpHost用於定義接口跟地址

  4. 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();
    }
}
相關文章
相關標籤/搜索