手把手教你用C#对接爱发电API:Afdian.Sdk保姆级教程(含Webhook通知实战)
在独立开发者和小团队的项目中,赞助功能往往是维持项目可持续发展的重要途径。爱发电作为国内知名的创作者赞助平台,为开发者提供了便捷的赞助接入方案。而Afdian.Sdk这个非官方.NET库,则让C#开发者能够以更优雅的方式集成爱发电功能。本文将带你从零开始,以一个开源工具项目为例,完整实现从SDK集成到Webhook处理的全流程。
1. 环境准备与基础配置
在开始之前,确保你的开发环境满足以下要求:
- .NET 6.0或更高版本
- Visual Studio 2022或Rider最新版
- 一个已注册的爱发电创作者账号
首先通过NuGet安装Afdian.Sdk:
dotnet add package Afdian.Sdk接下来需要获取爱发电API的必要凭证:
- 登录爱发电开发者后台
- 进入"应用管理"→"API密钥"
- 记录下你的
userId和token
建议将这些敏感信息存储在安全的地方,而不是硬编码在项目中。对于.NET项目,可以使用secrets.json:
{ "SecretsKeys": { "AfdianToken": "你的token", "AfdianUserId": "你的userId" } }2. 核心API调用实战
2.1 初始化客户端
创建AfdianClient实例是使用SDK的第一步:
using Afdian.Sdk; var afdianClient = new AfdianClient( userId: Configuration["SecretsKeys:AfdianUserId"], token: Configuration["SecretsKeys:AfdianToken"] );2.2 基础功能测试
在正式开发前,建议先进行连通性测试:
// 测试API连通性 string pingResult = afdianClient.Ping(); Console.WriteLine($"API连通性测试结果: {pingResult}"); // 或者使用异步版本 var pingResultAsync = await afdianClient.PingAsync();2.3 查询订单与赞助者
获取赞助数据是核心需求,SDK提供了多种查询方式:
// 查询第一页订单(返回原始JSON字符串) string ordersJson = afdianClient.QueryOrder(page: 1); // 查询赞助者(返回强类型模型) var sponsors = afdianClient.QuerySponsorModel(page: 1); // 异步查询示例 var recentOrders = await afdianClient.QueryOrderModelAsync(page: 1, pageSize: 20);实际项目中,你可能需要处理分页和错误情况:
try { int currentPage = 1; bool hasMore = true; var allOrders = new List<Order>(); while (hasMore) { var pageResult = await afdianClient.QueryOrderModelAsync(currentPage); allOrders.AddRange(pageResult.Data.List); hasMore = pageResult.Data.TotalPage > currentPage; currentPage++; // 避免频繁调用 await Task.Delay(500); } Console.WriteLine($"共获取到{allOrders.Count}条订单记录"); } catch (Exception ex) { Console.WriteLine($"查询订单时出错: {ex.Message}"); }3. Webhook通知集成
Webhook是实时获取赞助通知的最佳方式。下面我们实现一个完整的Webhook处理流程。
3.1 配置Webhook端点
首先在爱发电后台设置Webhook地址:
- 进入开发者后台→Webhook设置
- 添加一个新的Webhook端点
- 设置一个强密码用于验证签名
3.2 实现Webhook控制器
创建一个ASP.NET Core控制器处理Webhook通知:
[ApiController] [Route("api/webhook")] public class AfdianWebhookController : ControllerBase { private readonly IConfiguration _config; private readonly TelegramService _telegram; public AfdianWebhookController(IConfiguration config, TelegramService telegram) { _config = config; _telegram = telegram; } [HttpPost] public async Task<IActionResult> HandleNotification([FromBody] AfdianWebhookPayload payload) { // 1. 验证签名 string secret = _config["AfdianWebhookSecret"]; if (!VerifySignature(payload, secret)) { return Unauthorized(); } // 2. 处理不同类型的事件 switch (payload.Data.Type) { case "order.new": await HandleNewOrder(payload.Data.Order); break; case "order.changed": await HandleOrderUpdate(payload.Data.Order); break; default: Console.WriteLine($"未知事件类型: {payload.Data.Type}"); break; } return Ok(new { ec = 200, em = "success" }); } private bool VerifySignature(AfdianWebhookPayload payload, string secret) { // 实现签名验证逻辑 } private async Task HandleNewOrder(Order order) { string message = $"🎉 新赞助: {order.Sponsor.Name} 赞助了 {order.Amount}元"; await _telegram.SendMessageAsync(message); // 这里可以添加更多处理逻辑,如更新数据库等 } }3.3 发送Telegram通知
集成Telegram通知可以让开发者实时获知赞助情况:
public class TelegramService { private readonly HttpClient _httpClient; private readonly string _botToken; private readonly string _chatId; public TelegramService(IConfiguration config) { _botToken = config["TelegramBotToken"]; _chatId = config["TelegramChatId"]; _httpClient = new HttpClient(); } public async Task SendMessageAsync(string text) { var url = $"https://api.telegram.org/bot{_botToken}/sendMessage"; var content = new FormUrlEncodedContent(new Dictionary<string, string> { ["chat_id"] = _chatId, ["text"] = text, ["parse_mode"] = "Markdown" }); await _httpClient.PostAsync(url, content); } }4. 进阶技巧与最佳实践
4.1 错误处理与重试机制
API调用可能会遇到各种网络问题,实现健壮的错误处理很重要:
public async Task<T> ExecuteWithRetry<T>(Func<Task<T>> action, int maxRetries = 3) { int retryCount = 0; while (true) { try { return await action(); } catch (HttpRequestException ex) when (retryCount < maxRetries) { retryCount++; var delay = Math.Pow(2, retryCount) * 100; // 指数退避 await Task.Delay((int)delay); } } } // 使用示例 var sponsors = await ExecuteWithRetry(() => afdianClient.QuerySponsorModelAsync(1));4.2 性能优化建议
当处理大量数据时,考虑以下优化:
- 使用
IAsyncEnumerable流式处理分页数据 - 实现本地缓存减少API调用
- 批量处理Webhook通知
public async IAsyncEnumerable<Order> GetAllOrdersAsync() { int page = 1; while (true) { var result = await afdianClient.QueryOrderModelAsync(page); foreach (var order in result.Data.List) { yield return order; } if (page >= result.Data.TotalPage) break; page++; } } // 使用示例 await foreach (var order in GetAllOrdersAsync()) { // 处理每个订单 }4.3 安全注意事项
在处理赞助数据时,安全至关重要:
- 始终验证Webhook签名
- 不要在前端暴露API密钥
- 定期轮换凭证
- 记录所有API调用和Webhook事件
private bool VerifySignature(AfdianWebhookPayload payload, string secret) { var paramsToSign = new Dictionary<string, string> { ["ts"] = payload.Ts.ToString(), ["body"] = JsonSerializer.Serialize(payload.Data) }; var sortedParams = paramsToSign.OrderBy(x => x.Key); var stringToSign = string.Join("", sortedParams.Select(x => $"{x.Key}{x.Value}")) + secret; using var sha256 = SHA256.Create(); var computedHash = sha256.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)); var computedSignature = BitConverter.ToString(computedHash).Replace("-", "").ToLower(); return computedSignature == payload.Signature; }5. 项目集成示例
让我们以一个开源Markdown编辑器项目为例,展示如何完整集成爱发电功能。
5.1 添加赞助徽章
在项目README中添加爱发电赞助徽章:
[](https://afdian.net/a/yourname)5.2 实现赞助者致谢页面
创建一个ASP.NET Core页面展示赞助者:
public class SponsorsModel : PageModel { private readonly AfdianClient _afdian; public List<Sponsor> TopSponsors { get; set; } public SponsorsModel(AfdianClient afdian) { _afdian = afdian; } public async Task OnGetAsync() { var result = await _afdian.QuerySponsorModelAsync(page: 1, pageSize: 10); TopSponsors = result.Data.List .OrderByDescending(s => s.AllSumAmount) .Take(5) .ToList(); } }对应的Razor页面:
@page @model SponsorsModel <h2>特别感谢以下赞助者</h2> <div class="sponsor-grid"> @foreach (var sponsor in Model.TopSponsors) { <div class="sponsor-card"> <img src="@sponsor.Avatar" alt="@sponsor.Name" /> <h3>@sponsor.Name</h3> <p>累计赞助: @sponsor.AllSumAmount 元</p> <p>@sponsor.Message</p> </div> } </div>5.3 自动化赞助者福利发放
对于提供赞助者专属功能的项目,可以实现自动化福利发放:
public class SponsorBenefitsService { private readonly AfdianClient _afdian; private readonly IUserRepository _userRepo; public SponsorBenefitsService(AfdianClient afdian, IUserRepository userRepo) { _afdian = afdian; _userRepo = userRepo; } public async Task UpdateBenefitsAsync() { var sponsors = await GetAllActiveSponsorsAsync(); foreach (var user in await _userRepo.GetAllAsync()) { var isSponsor = sponsors.Any(s => s.UserId == user.ExternalId); await _userRepo.UpdatePremiumStatusAsync(user.Id, isSponsor); } } private async Task<List<Sponsor>> GetAllActiveSponsorsAsync() { // 实现获取所有活跃赞助者的逻辑 } }