The Magicodes.Wx.Sdk aims to provide the most straightforward approach to handling WeChat integrations, covering Official Accounts, Mini Programs, and Enterprise WeChat, including support for Abp VNext modules. This guide focuses on how to contribute to the SDK by defining new API interfaces efficiently.
Core Dependency: WebApiClientCore
The simplicity of the SDK is largely atributed to the WebApiClientCore library. It handles the heavy lifting of HTTP request packaging, parameter mapping, and response validation. You can find the library at https://github.com/dotnetcore/WebApiClient.
Implementation Walkthrough
We will use the "Customer Service Account Management" feature (specifically adding an account) as a case study. The official documentation is available at WeChat Official Docs.
1. Define the API Interface
Create an interface that represents the remote service. By inheriting from IWxApiWithAccessTokenFilter, the SDK automatically appends the access_token query parameter to every request.
/// <summary>
/// Manages customer service accounts
/// </summary>
[HttpHost("https://api.weixin.qq.com/customservice/kfaccount/")]
public interface ICustomerServiceApi : IWxApiWithAccessTokenFilter
{
/// <summary>
/// Creates a new customer service account
/// </summary>
[HttpPost("add")]
Task<BaseWxResponse> CreateAccountAsync(NewAccountDto accountDto);
/// <summary>
/// Modifies existing account details
/// </summary>
[HttpPost("update")]
Task<BaseWxResponse> ModifyAccountAsync(NewAccountDto accountDto);
/// <summary>
/// Removes a customer service account
/// </summary>
[HttpPost("del")]
Task<BaseWxResponse> RemoveAccountAsync(DeleteAccountDto deleteDto);
}
Key Attributes:
2. Define Data Transfer Objects (DTOs)
Create classes to represent the input parameters. Use JsonProperty to map C# properties to JSON keys required by WeChat.
public class NewAccountDto
{
/// <summary>
/// Account format: prefix@wechatid
/// </summary>
[JsonProperty("kf_account")]
[Required]
[StringLength(30)]
public string AccountId { get; set; }
/// <summary>
/// Nickname for the support agent
/// </summary>
[JsonProperty("nickname")]
[StringLength(16)]
public string AgentName { get; set; }
}
Tip: You can use Visual Studio's "Paste JSON as Classes" feature (Edit > Paste Special) to generate these DTOs quickly from API samples.
3. Handle API Responses
If the endpoint returns specific data, create a response class inheriting from BaseWxResponse. If it only returns a standard success/error code, use BaseWxResponse directly.
public class AuthTokenResponse : BaseWxResponse
{
/// <summary>
/// The token value
/// </summary>
[JsonProperty("access_token")]
public string Token { get; set; }
/// <summary>
/// Validity duration in seconds
/// </summary>
[JsonProperty("expires_in")]
public int LifeSpan { get; set; }
}
4. Unit Testing
Verify the implementation using xUnit and the test factory provided by the SDK.
public class NotificationApiTest : TestBase, IClassFixture<TestWebApplicationFactory>
{
private readonly INotificationApi notificationApi;
public NotificationApiTest(TestWebApplicationFactory factory, ITestOutputHelper output) : base(factory, output)
{
notificationApi = GetRequiredService<INotificationApi>();
}
[Fact]
public async Task NotifyUser_ShouldSucceed()
{
var response = await notificationApi.SendAsync(new NotificationInput
{
Recipient = "oXELNwzZgamuLS0JrJhVgdelzKyw",
TemplateId = "riid7aad8OKRQD9Ey6dclWBBkrqZSFDhlpKh0_spGLA",
Payload = new Dictionary<string, TemplateDataItem>
{
{"first", new TemplateDataItem("Test")},
{"keyword1", new TemplateDataItem("User")}
}
});
// Throws WxSdkException if the API call failed
response.EnsureSuccess();
}
}
Behind the Scenes
Access Token Management
The IWxApiWithAccessTokenFilter interface acts as a marker that triggers the AccessTokenApiFilter. This filter intercepts outgoing requests to inject the token automatically.
[JsonNetReturn(EnsureMatchAcceptContentType = false)]
[AccessTokenApiFilter]
public interface IWxApiWithAccessTokenFilter { }
The filter logic retrieves the token from the ITokenManager and appends it to the query string:
public class AccessTokenApiFilter : ApiFilterAttribute
{
public override async Task OnRequestAsync(ApiRequestContext ctx)
{
var manager = ctx.HttpContext.ServiceProvider.GetRequiredService<ITokenManager>();
var token = await manager.GetAccessTokenAsync();
ctx.HttpContext.RequestMessage.AddUrlQuery("access_token", token);
}
public override Task OnResponseAsync(ApiResponseContext ctx) => Task.CompletedTask;
}
Base Response Structure
All standard responses inherit from BaseWxResponse, which handles error code parsing and success verification.
public class BaseWxResponse
{
[JsonProperty("errcode")]
public virtual ReturnCodes Code { get; set; }
[JsonProperty("errmsg")]
public virtual string ErrorMessage { get; set; }
public virtual bool IsSuccess() => Code == ReturnCodes.请求成功;
public virtual string GetFriendlyMessage() => Code.ToString();
}