Mud.Feishu.Webhook
3.0.0
dotnet add package Mud.Feishu.Webhook --version 3.0.0
NuGet\Install-Package Mud.Feishu.Webhook -Version 3.0.0
<PackageReference Include="Mud.Feishu.Webhook" Version="3.0.0" />
<PackageVersion Include="Mud.Feishu.Webhook" Version="3.0.0" />
<PackageReference Include="Mud.Feishu.Webhook" />
paket add Mud.Feishu.Webhook --version 3.0.0
#r "nuget: Mud.Feishu.Webhook, 3.0.0"
#:package Mud.Feishu.Webhook@3.0.0
#addin nuget:?package=Mud.Feishu.Webhook&version=3.0.0
#tool nuget:?package=Mud.Feishu.Webhook&version=3.0.0
Mud.Feishu.Webhook
飞书事件订阅与处理的 Webhook 组件,提供完整的飞书事件接收、验证、解密和分发功能。
🚀 新特性:极简API - 一行代码完成服务注册,开箱即用!
功能特性
🚀 核心能力
- ✅ 极简API:一行代码完成服务注册,开箱即用
- ✅ 灵活配置:支持配置文件、代码配置和建造者模式
- ✅ 自动事件路由:根据事件类型自动分发到对应的处理器
- ✅ 中间件模式:使用 .NET 标准中间件模式,集成简单
- ✅ 依赖注入:完全集成 .NET 依赖注入容器
🔒 安全防护
- ✅ 安全验证:支持事件订阅验证、请求签名验证和时间戳验证
- ✅ 加密解密:内置 AES-256-CBC 解密功能,自动处理飞书加密事件
- ✅ 安全加固:强化 IP 验证、签名验证和密钥安全检查
- ✅ 请求频率限制:内置滑动窗口限流中间件,防止恶意请求
- ✅ 内容安全:仅接受
application/json请求,防止 DoS 攻击 - ✅ 日志脱敏:自动脱敏敏感字段防止信息泄露
⚡ 性能与可靠性
- ✅ 异步处理:完全异步的事件处理机制
- ✅ 并发控制:可配置的并发事件处理数量限制,支持热更新
- ✅ 后台处理模式:支持异步后台处理,避免飞书超时重试
- ✅ 容错机制:失败事件指数退避重试
- ✅ 分布式支持:提供分布式去重接口,支持 Redis 等外部存储
📊 监控与运维
- ✅ 异常处理:完善的异常处理和日志记录
- ✅ 性能监控:可选的性能指标收集和监控
- ✅ 健康检查:内置健康检查端点
- ✅ 配置热更新:支持运行时配置变更,无需重启服务
- ✅ 内存管理:Nonce 过期清理,防止内存泄漏
- ✅ 配置锁定:生产环境强制安全检查
🌐 兼容性与扩展
- ✅ 跨平台兼容:支持 .NET Standard 2.0、.NET 6.0、.NET 8.0、.NET 10.0
- ✅ 原生 AOT 支持:net8.0+ 一等公民支持 Native AOT 发布,源生成 JSON 序列化与配置绑定
- ✅ 多应用支持:支持多个飞书应用共享同一个 Webhook 端点
- ✅ 事件处理拦截器:前置/后置事件处理拦截器机制
- ✅ 流式处理:流式请求体读取,优化内存使用
快速开始
1. 安装 NuGet 包
dotnet add package Mud.Feishu.Webhook
2. 最简配置(一行代码)
在 Program.cs 中:
using Mud.Feishu.Webhook;
using Mud.Feishu.Webhook.Extensions;
var builder = WebApplication.CreateBuilder(args);
// 一行代码注册Webhook服务(需要至少一个事件处理器)
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.AddHandler<MessageEventHandler>()
.Build();
var app = builder.Build();
// 添加飞书Webhook限流中间件(可选,推荐在生产环境启用)
app.UseFeishuRateLimit();
// 添加飞书Webhook中间件
app.UseFeishuWebhook();
app.Run();
💡 说明:Webhook 服务使用中间件模式,通过
app.UseFeishuWebhook()自动注册端点。默认路由为/feishu/{AppKey},其中{AppKey}为应用键。⚠️ 注意:限流中间件应该在 Webhook 中间件之前注册,以确保限流策略能够正确应用。
3. 完整配置(添加多个事件处理器)
using Mud.Feishu.Webhook.Extensions;
var builder = WebApplication.CreateBuilder(args);
// 注册多个事件处理器
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.AddHandler<MessageReceiveEventHandler>() // 消息接收事件
.AddHandler<DepartmentCreatedEventHandler>() // 部门创建事件
.AddHandler<DepartmentUpdateEventHandler>() // 部门更新事件
.AddHandler<DepartmentDeleteEventHandler>() // 部门删除事件
.Build();
var app = builder.Build();
// 添加飞书Webhook中间件
app.UseFeishuWebhook();
app.Run();
4. 配置文件(appsettings.json)
{
"FeishuWebhook": {
"GlobalRoutePrefix": "feishu",
"AutoRegisterEndpoint": true,
"EnableExceptionHandling": true,
"EventHandlingTimeoutMs": 30000,
"MaxConcurrentEvents": 10,
"AllowedHttpMethods": ["POST"],
"MaxRequestBodySize": 10485760,
"AllowedSourceIPs": [],
"EnforceHeaderSignatureValidation": true,
"TimestampToleranceSeconds": 30,
"NonceValidationFailureMode": "Reject",
"EnableTokenBackgroundRefresh": null,
"Retry": {
"EnableRetry": false,
"MaxRetryCount": 3,
"InitialRetryDelaySeconds": 10,
"RetryDelayMultiplier": 2.0,
"MaxRetryDelaySeconds": 300,
"RetryPollIntervalSeconds": 30,
"MaxRetryPerPoll": 10
},
"RateLimit": {
"EnableRateLimit": false,
"WindowSizeSeconds": 60,
"MaxRequestsPerWindow": 100,
"EnableIpRateLimit": true,
"TooManyRequestsStatusCode": 429,
"TooManyRequestsMessage": "请求过于频繁,请稍后再试",
"WhitelistIPs": ["127.0.0.1", "::1"]
},
"Apps": {
"app1": {
"AppKey": "cli_a1b2c3d4e5f6g7h8",
"VerificationToken": "your_app1_verification_token",
"EncryptKey": "your_app1_encrypt_key_32_bytes_long"
},
"app2": {
"AppKey": "cli_h8g7f6e5d4c3b2a1",
"VerificationToken": "your_app2_verification_token",
"EncryptKey": "your_app2_encrypt_key_32_bytes_long"
}
}
}
}
🏗️ 服务注册方式
🚀 方式一:从配置文件注册(推荐)
using Mud.Feishu.Webhook.Extensions;
var builder = WebApplication.CreateBuilder(args);
// 从 appsettings.json 读取配置
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.AddHandler<MessageReceiveEventHandler>()
.Build();
var app = builder.Build();
app.UseFeishuWebhook();
app.Run();
⚙️ 方式二:代码配置
using Mud.Feishu.Webhook.Extensions;
var builder = WebApplication.CreateBuilder(args);
// 通过代码配置
builder.Services.CreateFeishuWebhookServiceBuilder(options =>
{
options.GlobalRoutePrefix = "feishu";
options.EnableExceptionHandling = true;
options.MaxConcurrentEvents = 10;
})
.AddHandler<MessageEventHandler>()
.Build();
var app = builder.Build();
app.UseFeishuWebhook();
app.Run();
注意:多应用场景下,请在 FeishuWebhookOptions 的 Apps 字典中配置每个应用的 VerificationToken 和 EncryptKey,或通过配置文件设置。
🔌 方式三:添加事件拦截器
using Mud.Feishu.Webhook.Extensions;
using Mud.Feishu.Abstractions.Interceptors;
var builder = WebApplication.CreateBuilder(args);
// 添加内置和自定义拦截器
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.AddInterceptor<LoggingEventInterceptor>() // 内置日志拦截器
.AddInterceptor<TelemetryEventInterceptor>() // 内置遥测拦截器
.AddInterceptor<AuditLogInterceptor>() // 自定义审计拦截器
.AddHandler<MessageEventHandler>()
.Build();
var app = builder.Build();
app.UseFeishuWebhook();
app.Run();
🔧 方式四:高级建造者模式
using Mud.Feishu.Webhook.Extensions;
var builder = WebApplication.CreateBuilder(args);
// 使用建造者模式进行复杂配置
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.EnableHealthChecks() // 启用健康检查
.AddHandler<MessageReceiveEventHandler>()
.AddHandler<DepartmentCreatedEventHandler>()
.Build();
var app = builder.Build();
// 添加健康检查端点
app.MapHealthChecks("/health");
app.UseFeishuWebhook();
app.Run();
🔥 方式五:指定配置节名称
using Mud.Feishu.Webhook.Extensions;
var builder = WebApplication.CreateBuilder(args);
// 从自定义配置节读取
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration, "MyFeishuConfig")
.AddHandler<MessageEventHandler>()
.Build();
var app = builder.Build();
app.UseFeishuWebhook();
app.Run();
使用模式
中间件模式
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.AddHandler<MessageEventHandler>()
.Build();
var app = builder.Build();
app.UseFeishuWebhook(); // 自动处理路由前缀下的请求
app.Run();
💡 说明:Webhook 服务目前仅支持中间件模式,通过配置
GlobalRoutePrefix(默认"feishu")来自定义路由路径。
创建事件处理器
方式一:实现 IFeishuEventHandler 接口
SupportedEventType约定:
- 返回非空值时,处理器只接收该类型的事件(必须与飞书的事件类型字符串完全一致, 拼错会导致事件被跳过且不产生任何业务效果)。
- 返回空串 / null 表示"处理该应用(或全局)的全部事件"。
- 应用专属处理器全部因类型不匹配而跳过时,SDK 会输出 Warning 并记录
unhandled指标——请据此核对拼写(生产默认日志级别即可看到)。- 处理器应协作式响应
CancellationToken:超时是软超时,不会强制中断处理器。
using Microsoft.Extensions.Logging;
using Mud.Feishu.Abstractions;
using System.Text.Json;
public class MessageReceiveEventHandler : IFeishuEventHandler
{
private readonly ILogger<MessageReceiveEventHandler> _logger;
public MessageReceiveEventHandler(ILogger<MessageReceiveEventHandler> logger)
{
_logger = logger;
}
// 指定支持的事件类型
public string SupportedEventType => "im.message.receive_v1";
public async Task HandleAsync(EventData eventData, CancellationToken cancellationToken = default)
{
_logger.LogInformation("收到消息事件: EventId={EventId}, EventType={EventType}",
eventData.EventId, eventData.EventType);
// 处理消息逻辑
var messageData = JsonSerializer.Deserialize<MessageEventData>(
eventData.Event?.ToString() ?? string.Empty);
// 你的业务逻辑...
_logger.LogInformation("处理消息: {MessageId}", messageData?.MessageId);
await Task.CompletedTask;
}
}
public class MessageEventData
{
public string MessageId { get; set; }
public string Content { get; set; }
// ... 其他字段
}
方式二:继承基类处理器(推荐)
基类处理器(如 DepartmentCreatedEventHandler)由 [GenerateEventHandler] 源生成器生成,位于 Mud.Feishu.EventCallback 命名空间,提供类型安全和自动去重。
⚠️ 注意:
Mud.Feishu.Webhook包本身不依赖Mud.Feishu.EventCallback,使用前需要自行引用Mud.Feishu.EventCallback包。
using Mud.Feishu.Abstractions;
using Mud.Feishu.Abstractions.DataModels.Organization;
using Mud.Feishu.Abstractions.EventHandlers;
using Mud.Feishu.Abstractions.Services;
using Mud.Feishu.EventCallback; // 源生成的基类所在命名空间
using Mud.Feishu.EventCallback.Organization; // DepartmentCreatedResult
/// <summary>
/// 部门创建事件处理器
/// </summary>
public class DemoDepartmentEventHandler : DepartmentCreatedEventHandler
{
private readonly DemoEventService _eventService;
public DemoDepartmentEventHandler(
IFeishuEventDeduplicator businessDeduplicator,
ILogger<DemoDepartmentEventHandler> logger,
DemoEventService eventService)
: base(businessDeduplicator, logger) // 第三参为可选的 IAppKeyAccessor
{
_eventService = eventService;
}
protected override async Task ProcessBusinessLogicAsync(
EventData eventData,
DepartmentCreatedResult? eventEntity,
FeishuEventHeader? header,
CancellationToken cancellationToken = default)
{
// 事件实体字段位于 eventEntity.Object 上
_logger.LogInformation("处理部门创建事件: 部门ID={DepartmentId}, 部门名={DepartmentName}",
eventEntity?.Object?.DepartmentId, eventEntity?.Object?.Name);
if (eventEntity?.Object is null)
{
return;
}
// 你的业务逻辑
await _eventService.RecordDepartmentEventAsync(eventEntity, cancellationToken);
// 模拟权限初始化
_logger.LogInformation("初始化部门权限: {DepartmentName}", eventEntity.Object.Name);
// 模拟通知部门主管
if (!string.IsNullOrWhiteSpace(eventEntity.Object.LeaderUserId))
{
_logger.LogInformation("通知部门主管: {LeaderUserId}", eventEntity.Object.LeaderUserId);
}
}
}
💡 提示:基类的
HandleAsync方法为sealed(内置自动去重逻辑),不可重写;扩展时只需重写ProcessBusinessLogicAsync。
可用的基类事件处理器
DepartmentCreatedEventHandler- 部门创建事件DepartmentUpdateEventHandler- 部门更新事件DepartmentDeleteEventHandler- 部门删除事件- 更多处理器请参考
Mud.Feishu.EventCallback命名空间
配置选项
基本配置
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
VerificationToken |
string | - | 飞书事件订阅验证 Token |
EncryptKey |
string | - | 飞书事件加密密钥(32字节) |
GlobalRoutePrefix |
string | "feishu" | 全局路由前缀(所有应用共享的基础路径) |
AutoRegisterEndpoint |
bool | true | 是否自动注册端点 |
多应用配置
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
Apps |
Dictionary<string, FeishuAppWebhookOptions> | {} | 应用配置集合(AppKey → 应用配置) |
Apps.{AppKey}.AppKey |
string | - | 应用键(用于标识应用,仅允许字母、数字、下划线和连字符) |
Apps.{AppKey}.VerificationToken |
string | - | 应用验证 Token |
Apps.{AppKey}.EncryptKey |
string | - | 应用加密 Key(32字节) |
Apps.{AppKey}.TimestampToleranceSeconds |
int? | null | 时间戳容差(null 继承全局;-1/0 为兼容写法,同样继承全局) |
Apps.{AppKey}.EventHandlingTimeoutMs |
int? | null | 事件处理超时(null 继承全局;-1/0 为兼容写法,同样继承全局) |
Apps.{AppKey}.EnforceHeaderSignatureValidation |
bool? | null | 是否强制签名验证(null 继承全局) |
Apps.{AppKey}.EnableExceptionHandling |
bool? | null | 是否启用异常处理(null 继承全局) |
安全配置
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
AllowedSourceIPs |
HashSet<string> | [] | 允许的源 IP 地址列表(非空时自动启用 IP 验证,支持 CIDR 与 IPv4-mapped IPv6) |
AllowedHttpMethods |
HashSet<string> | ["POST"] | 允许的 HTTP 方法 |
MaxRequestBodySize |
long | 10MB | 最大请求体大小 |
EnforceHeaderSignatureValidation |
bool | true | 是否强制验证请求头签名(生产环境禁止关闭,含应用级覆盖) |
TimestampToleranceSeconds |
int | 30 | 时间戳验证容错范围(秒),上限 300 秒(重放窗口上限) |
NonceValidationFailureMode |
NonceFailureMode | Reject | Nonce 去重服务不可用时的降级策略(Reject=安全优先拒绝,Allow=可用性优先放行) |
RejectEmptyIdentifiers |
bool | true | 空 EventId/Nonce 是否拒绝请求(fail-closed) |
IgnoreUnknownEventTypes |
bool | true | 未注册 eventType 的事件是否静默忽略(记 Warning + unhandled 指标) |
AllowInMemoryNonceDedupInProduction |
bool | false | 生产环境是否允许使用进程内内存 Nonce 去重(见下方「部署形态约束」) |
InterceptionAckMode |
InterceptionAckMode | Ack | 拦截器中断事件时对飞书表达的确认语义(见「拦截器中断语义」) |
InterceptorFallbackMode |
InterceptorFallbackMode | Merge | 应用专属拦截器与全局拦截器的组合策略(见「拦截器执行顺序」) |
NonceTtlSeconds |
int? | null | Nonce 有效期(秒);显式配置时强制 严格大于 TimestampToleranceSeconds |
重放窗口不变量:
NonceTtl(Redis 工程RedisOptions)必须 ≥TimestampToleranceSeconds, 否则在 Nonce 过期后、容差窗口结束前的区间内重放攻击可行。默认组合(NonceTtl=5min / 容差上限=300s)天然满足;跨工程配置无法在单一库内联断言,由两侧 XML 文档共同声明。
⚠️ 生产环境 Nonce 去重形态:生产环境(
ASPNETCORE_ENVIRONMENT=Production)若未注册 分布式 Nonce 去重实现(AddFeishuRedisDeduplicators()),宿主启动即失败。 确为单实例部署时,显式设置AllowInMemoryNonceDedupInProduction=true承担风险。
部署形态约束(多实例必读)
内存实现只对单实例有效——多实例各进程的内存表互不相通。
| 能力 | 单实例 | 多实例(负载均衡) |
|---|---|---|
| Nonce 防重放 | 内存可用(生产需显式豁免) | 必须 Redis,否则跨实例重放不可检测 |
| 事件去重(EventId) | 内存可用 | 建议 Redis(否则重复消费面扩大) |
| 请求限流 | 内存可用 | 每实例独立(等效配额 ×N) |
| 并发闸 | 单进程有效 | 每实例独立 |
| 失败事件重投 | 进程内(重启即丢) | 需自定义 IFailedEventStore |
性能配置
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
MaxConcurrentEvents |
int | 10 | 最大并发事件数,支持热更新 |
EventHandlingTimeoutMs |
int | 30000 | 事件处理软超时(毫秒)——仅取消令牌、不中断处理器,详见下方说明 |
EnableTokenBackgroundRefresh |
bool? | null | 令牌后台刷新显式覆盖(null=不干预基座;R4 已移除 EnableBackgroundProcessing) |
⚠️
EventHandlingTimeoutMs是"软超时",不是硬超时:到达该时限时 SDK 只取消CancellationToken,不会强制中断处理器(强制中断会造成"去重状态已释放而任务仍在跑"的双重执行)。 因此只有协作式响应取消令牌的处理器才受该值约束;不响应取消的处理器会持续占用本次请求的 并发闸槽位与去重processing态,实际耗时可远超该值。 此类情况会以timeout_overshoot指标与 Warning 日志暴露,请据此排查处理器实现。
拦截器中断语义
BeforeHandleAsync 返回 false 时事件被中断,其对飞书表达的语义由 InterceptionAckMode 决定:
InterceptionAckMode |
事件已消费 | HTTP | 落去重 | 说明 |
|---|---|---|---|---|
Ack(默认) |
是 | 200 | 是 | 拦截 = 有意消费,飞书不再重推 |
Retryable |
否 | 503 | 否 | 拦截 = 暂时不能处理,要求对端稍后重推 |
两者都会写入指标(intercepted / intercepted_retryable),并可通过 AfterHandleAsync
收到的 EventHandlingOutcomeException.OutcomeKind 判别。
日志配置
日志级别统一由 Logging:LogLevel:Mud.Feishu.Webhook 控制,不存在模块私有日志开关。
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
EnableExceptionHandling |
bool | true | 是否吞并事件处理异常(错误处理策略,非日志开关) |
失败事件重试配置
R5.1 起真实生效(写入侧与轮询侧同源于
FeishuWebhookOptions.Retry);此前除EnableRetry外 其余键静默无效,升级前请核对取值,详见documents/Configuration/ConfigMigration-R5.md。
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
Retry.EnableRetry |
bool | false | 是否启用失败事件重试 |
Retry.MaxRetryCount |
int | 3 | 最大重试次数 |
Retry.InitialRetryDelaySeconds |
int | 10 | 初始重试延迟(秒) |
Retry.RetryDelayMultiplier |
double | 2.0 | 重试延迟倍数(指数退避) |
Retry.MaxRetryDelaySeconds |
int | 300 | 最大重试延迟(秒) |
Retry.RetryPollIntervalSeconds |
int | 30 | 重试轮询间隔(秒) |
Retry.MaxRetryPerPoll |
int | 10 | 每次轮询处理的最大失败事件数 |
请求频率限制配置
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
RateLimit.EnableRateLimit |
bool | false | 是否启用请求频率限制 |
RateLimit.WindowSizeSeconds |
int | 60 | 时间窗口大小(秒) |
RateLimit.MaxRequestsPerWindow |
int | 100 | 每个时间窗口内允许的最大请求数 |
RateLimit.EnableIpRateLimit |
bool | true | 是否基于 IP 限流 |
RateLimit.TooManyRequestsStatusCode |
int | 429 | 超出限制时的响应状态码 |
RateLimit.TooManyRequestsMessage |
string | "请求过于频繁,请稍后再试" | 超出限制时的响应消息 |
RateLimit.WhitelistIPs |
HashSet<string> | [] | 白名单 IP 列表(不参与限流) |
高级功能
多应用支持
支持多个飞书应用共享同一个 Webhook 端点:
{
"FeishuWebhook": {
"Apps": {
"app1": {
"AppKey": "cli_a1b2c3d4e5f6g7h8",
"VerificationToken": "your_app1_verification_token",
"EncryptKey": "your_app1_encrypt_key_32_bytes_long"
},
"app2": {
"AppKey": "cli_h8g7f6e5d4c3b2a1",
"VerificationToken": "your_app2_verification_token",
"EncryptKey": "your_app2_encrypt_key_32_bytes_long"
}
}
}
}
每个应用的路由将自动注册为 /feishu/{AppKey}。
请求频率限制
内置滑动窗口限流中间件,防止恶意请求:
{
"FeishuWebhook": {
"RateLimit": {
"EnableRateLimit": true,
"WindowSizeSeconds": 60,
"MaxRequestsPerWindow": 100,
"EnableIpRateLimit": true,
"WhitelistIPs": ["127.0.0.1", "::1"]
}
}
}
多应用支持:限流策略基于 (AppKey, IP) 维度,不同应用的请求不会相互影响。
使用方式:
var app = builder.Build();
// 添加飞书Webhook限流中间件(可选,推荐在生产环境启用)
app.UseFeishuRateLimit();
// 添加飞书Webhook中间件
app.UseFeishuWebhook();
app.Run();
后台处理模式
启用后台处理模式,避免飞书超时重试:
{
"FeishuWebhook": {
"EnableTokenBackgroundRefresh": true
}
}
// 启用后台处理模式后,中间件会立即返回成功响应
// 然后在后台异步处理事件,适用于耗时较长的业务逻辑
builder.Services.CreateFeishuWebhookServiceBuilder(options =>
{
options.EnableTokenBackgroundRefresh = true;
}).AddHandler<LongRunningEventHandler>()
.Build();
事件拦截器(Interceptors)
事件拦截器允许在事件处理前后执行自定义逻辑,如日志记录、指标收集、权限验证等。
内置拦截器
LoggingEventInterceptor - 记录事件处理日志
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.AddInterceptor<LoggingEventInterceptor>() // 记录事件处理开始和结束
.AddHandler<MessageEventHandler>()
.Build();
TelemetryEventInterceptor - 遥测数据收集
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.AddInterceptor<TelemetryEventInterceptor>(sp =>
new TelemetryEventInterceptor("My.Application")) // 指定应用名称
.AddHandler<MessageEventHandler>()
.Build();
自定义拦截器
创建自定义拦截器需要实现 IFeishuEventInterceptor 接口:
using Mud.Feishu.Abstractions;
/// <summary>
/// 审计日志拦截器示例
/// </summary>
public class AuditLogInterceptor : IFeishuEventInterceptor
{
private readonly ILogger<AuditLogInterceptor> _logger;
public AuditLogInterceptor(ILogger<AuditLogInterceptor> logger)
=> _logger = logger;
/// <summary>
/// 事件处理前拦截
/// </summary>
/// <returns>返回 false 将中断事件处理流程</returns>
public Task<bool> BeforeHandleAsync(string eventType, EventData eventData, CancellationToken cancellationToken = default)
{
_logger.LogInformation("[审计] 事件开始: {EventType}, EventId: {EventId}, TenantKey: {TenantKey}",
eventType, eventData.EventId, eventData.TenantKey);
return Task.FromResult(true); // 返回 true 继续处理,false 中断
}
/// <summary>
/// 事件处理后拦截
/// </summary>
public Task AfterHandleAsync(string eventType, EventData eventData, Exception? exception, CancellationToken cancellationToken = default)
{
if (exception == null)
{
_logger.LogInformation("[审计] 事件成功: {EventType}, EventId: {EventId}", eventType, eventData.EventId);
}
else
{
_logger.LogError(exception, "[审计] 事件失败: {EventType}, EventId: {EventId}", eventType, eventData.EventId);
}
return Task.CompletedTask;
}
}
注册自定义拦截器
// 类型注册
.AddInterceptor<AuditLogInterceptor>()
// 工厂注册
.AddInterceptor(sp => new AuditLogInterceptor(
sp.GetRequiredService<ILogger<AuditLogInterceptor>>()))
// 实例注册
var interceptor = new AuditLogInterceptor(logger);
.AddInterceptor(interceptor)
拦截器执行顺序
多应用下的组合策略(InterceptorFallbackMode):
| 值 | 行为 |
|---|---|
Merge(默认) |
全局拦截器先行,再执行应用专属拦截器 |
AppThenGlobal |
应用专属拦截器先行,再执行全局拦截器 |
AppOnly |
仅执行应用专属拦截器(旧行为,全局拦截器被丢弃并告警) |
⚠️ 旧行为(
AppOnly)会让安全/审计类全局拦截器在"已注册专属拦截器的应用"上静默失效。 升级后默认改为Merge;若确需旧语义,请显式配置AppOnly,启动期会列出被屏蔽的全局拦截器。 同一类型既全局注册又 app 专属注册时只执行一次(按类型去重)。
拦截器按注册顺序依次执行,完整流程:
Webhook 事件到达
↓
拦截器1: BeforeHandleAsync
↓
拦截器2: BeforeHandleAsync
↓
...
↓
拦截器N: BeforeHandleAsync
↓
[事件处理器处理事件]
↓
拦截器N: AfterHandleAsync
↓
...
↓
拦截器2: AfterHandleAsync
↓
拦截器1: AfterHandleAsync
↓
处理完成
应用场景
- 日志记录:记录事件处理的开始、成功、失败
- 指标收集:统计事件处理时间、成功率等
- 安全审计:记录敏感事件的处理情况
- 权限控制:根据事件类型或内容决定是否处理
- 性能监控:记录处理耗时,识别性能瓶颈
- 业务追踪:将事件信息写入审计日志或追踪系统
注册事件处理器
链式调用注册多个处理器
using Mud.Feishu.Webhook.Extensions;
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.AddHandler<MessageReceiveEventHandler>() // 消息接收
.AddHandler<DepartmentCreatedEventHandler>() // 部门创建
.AddHandler<DepartmentUpdateEventHandler>() // 部门更新
.AddHandler<DepartmentDeleteEventHandler>() // 部门删除
.Build();
使用工厂方法注册
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.AddHandler<MessageEventHandler>(sp =>
{
var logger = sp.GetRequiredService<ILogger<MessageEventHandler>>();
var myService = sp.GetRequiredService<MyCustomService>();
return new MessageEventHandler(logger, myService);
})
.Build();
使用实例注册
var handler = new MessageEventHandler(loggerFactory.CreateLogger<MessageEventHandler>());
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.AddHandler(handler)
.Build();
支持的事件类型
本库支持所有飞书开放平台的事件类型。常见事件类型包括:
消息事件
im.message.receive_v1- 接收消息事件im.message.recalled_v1- 消息撤回事件
群聊事件
im.chat.member_user.added_v1- 用户加入群聊im.chat.member_user.withdrawn_v1- 用户离开群聊im.chat.disbanded_v1- 群聊解散im.chat.updated_v1- 群信息变更
通讯录事件
contact.user.created_v3- 员工入职contact.user.updated_v3- 员工信息变更contact.user.deleted_v3- 员工离职contact.department.created_v3- 部门创建contact.department.updated_v3- 部门信息变更contact.department.deleted_v3- 部门删除
审批事件
approval.approval.approved_v1- 审批通过approval.approval.rejected_v1- 审批拒绝approval.approval.updated_v1- 审批更新
任务事件
task.task.created_v1- 任务创建task.task.updated_v1- 任务更新
💡 提示:更多事件类型请参考飞书开放平台事件列表
飞书平台配置
1. 创建事件订阅
- 登录飞书开放平台
- 进入你的应用详情页
- 点击"事件订阅"
- 配置请求网址:
https://your-domain.com/feishu/{your-appKey}(例如https://your-domain.com/feishu/myapp;URL 最后一段会被解析为 AppKey,用于匹配对应应用的多应用配置,请替换为你实际配置的 AppKey,不要写成固定的 "Webhook") - 设置验证 Token 和加密 Key
2. 配置事件类型
选择你需要订阅的事件类型:
- 消息事件
- 群聊事件
- 用户事件
- 部门事件
- 等...
3. 发布应用
配置完成后发布应用,飞书服务器将开始向你的端点推送事件。
自定义验证器和密钥提供程序
自定义签名验证器
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.UseSignatureValidator<MyCustomSignatureValidator>()
.AddHandler<MessageEventHandler>()
.Build();
自定义时间戳验证器
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.UseTimestampValidator<MyCustomTimestampValidator>()
.AddHandler<MessageEventHandler>()
.Build();
自定义 Nonce 验证器
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.UseNonceValidator<MyCustomNonceValidator>()
.AddHandler<MessageEventHandler>()
.Build();
自定义订阅验证器
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.UseSubscriptionValidator<MyCustomSubscriptionValidator>()
.AddHandler<MessageEventHandler>()
.Build();
自定义加密密钥提供程序
支持从外部源(如 Azure KeyVault、AWS Secrets Manager、环境变量等)获取加密密钥:
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.UseEncryptKeyProvider<AzureKeyVaultEncryptKeyProvider>()
.AddHandler<MessageEventHandler>()
.Build();
实现 IEncryptKeyProvider 接口:
using Mud.Feishu.Webhook;
public class AzureKeyVaultEncryptKeyProvider : IEncryptKeyProvider
{
private readonly KeyVaultClient _keyVaultClient;
public AzureKeyVaultEncryptKeyProvider(KeyVaultClient keyVaultClient)
{
_keyVaultClient = keyVaultClient;
}
public async Task<string?> GetEncryptKeyAsync(string appKey, CancellationToken cancellationToken = default)
{
return await _keyVaultClient.GetSecretAsync($"feishu-{appKey}-encrypt-key", cancellationToken);
}
public async Task<string?> GetVerificationTokenAsync(string appKey, CancellationToken cancellationToken = default)
{
return await _keyVaultClient.GetSecretAsync($"feishu-{appKey}-verification-token", cancellationToken);
}
}
自定义组合验证器
完全替换默认的验证逻辑:
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.UseCompositeValidator<MyCustomCompositeValidator>()
.AddHandler<MessageEventHandler>()
.Build();
应用级配置继承
多应用模式下,FeishuAppWebhookOptions 支持继承全局配置:
| 配置项 | 继承规则 |
|---|---|
TimestampToleranceSeconds |
设置为 -1 或 0 时继承全局配置,正整数使用应用级配置 |
EventHandlingTimeoutMs |
设置为 -1 或 0 时继承全局配置,正整数使用应用级配置 |
EnableExceptionHandling |
设置为 null 时继承全局配置,否则使用应用级配置 |
EnforceHeaderSignatureValidation |
设置为 null 时继承全局配置,否则使用应用级配置 |
示例:
{
"FeishuWebhook": {
"EventHandlingTimeoutMs": 30000,
"TimestampToleranceSeconds": 30,
"Apps": {
"app1": {
"EventHandlingTimeoutMs": 60000,
"TimestampToleranceSeconds": -1
},
"app2": {
"EventHandlingTimeoutMs": -1,
"TimestampToleranceSeconds": 60
}
}
}
}
上述配置中:
app1:事件处理超时 60 秒(应用级),时间戳容差 30 秒(继承全局)app2:事件处理超时 30 秒(继承全局),时间戳容差 60 秒(应用级)
安全模型与验证流程
签名算法
本组件使用 SHA-256 头部签名验证(与飞书官方 SDK Python/Go 一致),不使用 HMAC-SHA256。
签名字符串格式:SHA-256(timestamp + nonce + encryptKey + body)
签名通过请求头 X-Lark-Signature 传递,使用小写十六进制格式。
验证顺序
CompositeFeishuEventValidator 按以下顺序执行验证,任一步骤失败即拒绝请求:
1. 时间戳验证
└─ 检查请求时间戳是否在容差范围内(防重放攻击时间窗口)
2. Nonce 检查(仅检查,不标记)
└─ 检查 Nonce 是否已被使用(拦截重放攻击)
└─ 注意:此步骤不标记 Nonce,避免签名验证失败时 Nonce 被误消费
3. 签名验证
└─ 验证 X-Lark-Signature 请求头签名(SHA-256)
└─ 使用固定时间比较防止计时攻击
4. Nonce 标记(签名通过后标记)
└─ 签名验证通过后,标记 Nonce 为已使用
└─ 此时标记是安全的:不会因签名失败导致 Nonce 被误消费
└─ 并发场景:若标记时发现已被其他请求标记,拒绝请求
Nonce 两步验证设计
将 Nonce 验证拆分为"检查"(CheckNonceAsync)和"标记"(TryMarkNonceAsUsedAsync)两步:
- 检查步骤(签名验证前):仅检查 Nonce 是否已存在,不标记。拦截已使用的 Nonce(防重放攻击)
- 标记步骤(签名验证后):签名通过后才标记 Nonce 为已使用。确保签名验证失败时 Nonce 不会被消费,飞书可以安全重试
| 场景 | 变更前(旧逻辑) | 变更后(新逻辑) |
|---|---|---|
| 签名验证失败 | ❌ Nonce 已被消费,飞书重试时被拒绝(事件永久丢失) | ✅ Nonce 未被消费,飞书可安全重试 |
| 签名验证通过 | ✅ 正常标记 | ✅ 正常标记 |
| 重放攻击 | ✅ 被拦截 | ✅ 被拦截(检查步骤仍拦截已使用 Nonce) |
安全加固措施
| 措施 | 说明 |
|---|---|
| 固定时间比较 | Token 和签名比较使用 FixedTimeEquals,防止计时攻击 |
| 生产环境强制验证 | 生产环境自动检测并拒绝禁用签名验证的配置(含应用级 EnforceHeaderSignatureValidation=false 覆盖,启动期 fail-fast) |
| 重放窗口上限 | TimestampToleranceSeconds 上限 300 秒(飞书官方建议 ≤60 秒),应用级同受约束 |
| 空标识符 fail-closed | 空 EventId/Nonce 默认拒绝(RejectEmptyIdentifiers=true),畸形/恶意流量不进入业务链路 |
| 日志清洗 | nonce/eventId 等外部输入写入日志前经 LogSanitizer.Clean 清洗(防日志注入),超长截断 |
| Redis 故障语义分离 | 去重体系 Server 类致命故障返回 503(让飞书稍后重推),不伪装成 403/500;连接类故障按 NonceValidationFailureMode 降级 |
| 事件处理 at-least-once | 业务成功后完成标记(Mark)失败不回滚不报错,保留 processing 态由超时恢复/TTL 兜底——处理器需自身幂等(详见 IFeishuEventDeduplicator 语义声明) |
| 敏感信息掩码 | 日志中自动掩码 Token、EncryptKey 等敏感字段 |
| 请求体大小限制 | 防止超大请求体 DoS 攻击 |
| IP 白名单 | 支持配置允许的源 IP 地址列表(CIDR 格式,负数前缀/超宽前缀直接拒绝,IPv4-mapped IPv6 自动归一) |
| 频率限制 | 滑动窗口限流,基于 (AppKey, IP) 维度;满员时仅拒绝新键(已跟踪客户端不受伪造 IP 撑满字典影响) |
| 未注册事件忽略 | 未注册 eventType 默认静默忽略(IgnoreUnknownEventTypes=true),不回退到"第一个注册的处理器"兜底 |
安全审计
内置 ISecurityAuditService 安全审计服务,记录安全验证事件:
public class MySecurityService
{
private readonly ISecurityAuditService _auditService;
public MySecurityService(ISecurityAuditService auditService)
{
_auditService = auditService;
}
public async Task OnSuspiciousRequestAsync(string clientIp, string requestPath)
{
await _auditService.LogSecurityFailureAsync(
SecurityEventType.SignatureValidation,
clientIp,
requestPath,
"签名验证失败,疑似伪造请求");
}
}
支持的安全事件类型(SecurityEventType 枚举,当前版本共 4 种):SignatureValidation(签名验证)、TimestampValidation(时间戳防重放验证)、SubscriptionValidation(事件订阅 URL 验证)、Other(其他安全事件场景归入此类型)。
监控和诊断
性能监控
性能监控通过拦截器机制实现,支持 OpenTelemetry 和自定义指标收集:
// 添加遥测拦截器(OpenTelemetry 集成)
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.AddInterceptor<TelemetryEventInterceptor>()
.AddHandler<MessageEventHandler>()
.Build();
// 或使用自定义性能监控拦截器
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.AddInterceptor<PerformanceMonitoringInterceptor>()
.AddHandler<MessageEventHandler>()
.Build();
健康检查
健康检查除并发槽位与失败事件积压外,还暴露重放防护形态:
| 数据项 | 取值 | 说明 |
|---|---|---|
nonceDedup |
InMemory / Distributed |
Nonce 去重的实现形态 |
timestamp_tolerance_seconds |
数值 | 当前时间戳容差 |
生产环境(
ASPNETCORE_ENVIRONMENT=Production)使用内存 Nonce 去重时,健康状态判定为Degraded(多实例下跨实例重放不可检测)。这与启动期阻断、启动 Summary 日志共同构成 "启动可见 + 运行可见 + 指标可见"三层。
启动期会输出一次 Summary 日志,便于运维核对:
飞书 Webhook 启动自检 | Nonce 去重: InMemory | 事件去重: InMemory | 时间戳容差: 30s |
Nonce TTL: (未配置) | 重放窗口不变量(TTL ≥ 容差): 满足 | 应用数: 2
应用 appA 注册自检:处理器 [MessageHandler],拦截器 [(无)]
内置健康检查支持,默认启用,可监控 Webhook 服务的运行状态:
using Mud.Feishu.Webhook.Extensions;
var builder = WebApplication.CreateBuilder(args);
// 健康检查默认启用,也可以显式调用
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.EnableHealthChecks() // 启用健康检查(默认已启用)
.AddHandler<MessageEventHandler>()
.Build();
// 如需禁用健康检查
// builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
// .DisableHealthChecks()
// .AddHandler<MessageEventHandler>()
// .Build();
var app = builder.Build();
// 添加健康检查端点
app.MapHealthChecks("/health");
app.UseFeishuWebhook();
app.Run();
健康检查返回的数据包括:配置有效性、最大并发数、超时时间、当前可用并发槽位等。
日志记录
本库使用标准的 .NET 日志记录框架,可以灵活配置日志级别:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Mud.Feishu.Webhook": "Debug",
"Mud.Feishu.Webhook.Services": "Debug",
"Mud.Feishu.Webhook.Middleware": "Information",
"Mud.Feishu.Abstractions": "Information"
}
}
}
诊断端点(Demo 示例)
Demo 项目提供了诊断端点,可以查看已注册的事件处理器:
// 在 Demo 项目中使用
app.MapDiagnostics(); // 注册诊断端点
// 访问 /diagnostics/handlers 查看所有已注册的处理器
最佳实践
1. 错误处理
在事件处理器中妥善处理异常,避免影响其他事件的处理:
public class RobustEventHandler : IFeishuEventHandler
{
private readonly ILogger<RobustEventHandler> _logger;
public string SupportedEventType => "im.message.receive_v1";
public async Task HandleAsync(EventData eventData, CancellationToken cancellationToken = default)
{
try
{
_logger.LogInformation("开始处理事件: {EventId}", eventData.EventId);
// 你的业务逻辑
await ProcessBusinessLogicAsync(eventData, cancellationToken);
_logger.LogInformation("事件处理完成: {EventId}", eventData.EventId);
}
catch (OperationCanceledException)
{
_logger.LogWarning("事件处理被取消: {EventId}", eventData.EventId);
throw; // 超时取消应该抛出
}
catch (Exception ex)
{
_logger.LogError(ex, "处理事件时发生错误: {EventId}", eventData.EventId);
// 不要重新抛出异常,避免影响其他处理器
// 可以选择记录到失败队列或告警系统
}
}
private async Task ProcessBusinessLogicAsync(EventData eventData, CancellationToken cancellationToken)
{
// 实际业务逻辑
await Task.CompletedTask;
}
}
2. 异步处理和取消支持
正确使用异步编程和取消令牌:
public async Task HandleAsync(EventData eventData, CancellationToken cancellationToken = default)
{
// ✅ 正确:使用异步 API 并传递取消令牌
await ProcessMessageAsync(eventData, cancellationToken);
await SaveToDatabaseAsync(eventData, cancellationToken);
// ❌ 错误:不要使用阻塞调用
// var result = ProcessMessageAsync(eventData).Result;
// ProcessMessageAsync(eventData).Wait();
// ✅ 正确:尊重取消令牌
cancellationToken.ThrowIfCancellationRequested();
}
3. 依赖注入
合理使用依赖注入,确保服务生命周期正确:
public class MessageEventHandler : IFeishuEventHandler
{
private readonly ILogger<MessageEventHandler> _logger;
private readonly IMessageService _messageService; // Scoped 服务
private readonly IConfiguration _configuration; // Singleton 服务
public MessageEventHandler(
ILogger<MessageEventHandler> logger,
IMessageService messageService,
IConfiguration configuration)
{
_logger = logger;
_messageService = messageService;
_configuration = configuration;
}
public string SupportedEventType => "im.message.receive_v1";
public async Task HandleAsync(EventData eventData, CancellationToken cancellationToken = default)
{
// 使用注入的服务
await _messageService.ProcessAsync(eventData, cancellationToken);
}
}
4. 使用基类处理器(推荐)
继承基类处理器可以获得自动去重和类型安全:
using Mud.Feishu.EventCallback; // 源生成的基类所在命名空间(需引用 Mud.Feishu.EventCallback 包)
// 继承基类处理器,自动处理去重和类型转换
public class MyDepartmentHandler : DepartmentCreatedEventHandler
{
public MyDepartmentHandler(
IFeishuEventDeduplicator deduplicator,
ILogger<MyDepartmentHandler> logger)
: base(deduplicator, logger)
{
}
// 只需要实现业务逻辑;基类的 HandleAsync 为 sealed,不可重写
protected override async Task ProcessBusinessLogicAsync(
EventData eventData,
DepartmentCreatedResult? eventEntity,
FeishuEventHeader? header,
CancellationToken cancellationToken = default)
{
// eventEntity 已经是强类型的实体对象,事件字段位于 Object 上
_logger.LogInformation("处理部门: {Name}", eventEntity?.Object?.Name);
}
}
5. 配置验证
配置验证分两个阶段,尽早发现问题:
Build()阶段:只校验处理器注册——至少注册一个处理器、无重复的处理器/拦截器注册,违规时抛出InvalidOperationException。- 配置解析阶段:选项内容(Token、EncryptKey、应用级配置等)在
FeishuWebhookOptions被解析时通过PostConfigure触发Validate()校验,配置无效抛出InvalidOperationException。 自 R3-P0-5 起该校验在宿主启动期完成(由WebhookOptionsStartupValidator这一IHostedService显式触发), 配置错误一律表现为启动失败,不再推迟到第一个请求。
// Build() 只验证处理器注册
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.AddHandler<MessageEventHandler>()
.Build();
// 配置内容校验发生在首次解析 Options 时(PostConfigure -> Validate())
// 如果配置无效,会抛出 InvalidOperationException
6. 长时间运行的任务
对于耗时较长的任务,启用后台处理模式:
// appsettings.json
{
"FeishuWebhook": {
"EnableTokenBackgroundRefresh": true,
"EventHandlingTimeoutMs": 60000 // 增加超时时间
}
}
7. 测试和调试
Demo 项目提供了测试端点,可用于调试:
// 在 Demo 项目中
app.MapTestEndpoints(); // 测试端点
app.MapDiagnostics(); // 诊断端点
// 可以使用以下端点:
// POST /test/capture - 捕获原始请求
// GET /test/captured - 查看捕获的请求
// GET /diagnostics/handlers - 查看已注册的处理器
故障排除
常见问题
验证失败
- 检查
VerificationToken是否正确 - 确认请求 URL 配置正确
- 检查
解密失败
- 检查
EncryptKey是否正确 - 确认飞书平台已启用加密
- 检查
签名验证失败
- 检查时间同步
- 确认请求没有被代理服务器修改
- 生产环境确保
EnforceHeaderSignatureValidation设置为 true
事件处理失败
- 检查事件处理器是否正确注册
- 查看日志中的详细错误信息
分布式部署事件重复
- 默认使用内存去重,多实例部署需要实现分布式去重
- 参考
IFeishuNonceDistributedDeduplicator接口自定义 Redis 实现
超时处理
- 检查
EventHandlingTimeoutMs配置是否合理 - 确保事件处理逻辑支持取消令牌
- 检查
请求频率限制问题
- 检查
RateLimit.EnableRateLimit配置 - 确认客户端 IP 是否在白名单中
- 调整
MaxRequestsPerWindow和WindowSizeSeconds参数
- 检查
多应用配置问题
- 检查
Apps配置是否正确 - 确认各应用的 AppKey、VerificationToken 和 EncryptKey 配置
- 验证应用路由是否正确(
/feishu/{AppKey})
- 检查
调试技巧
// 启用详细日志
builder.Logging.AddConsole();
builder.Logging.SetMinimumLevel(LogLevel.Debug);
// 事件处理耗时日志以 Debug 级别无条件输出:把 Logging:LogLevel:Mud.Feishu.Webhook 设为 Debug 即可获得
builder.Services.CreateFeishuWebhookServiceBuilder(options =>
{
options.RateLimit.EnableRateLimit = true; // 启用限流调试
}).AddHandler<MessageEventHandler>()
.Build();
完整示例
基础示例
完整的 Program.cs 示例:
using Mud.Feishu.Webhook.Extensions;
using Mud.Feishu.Webhook.Demo.Handlers;
using Mud.Feishu.Webhook.Demo.Services;
var builder = WebApplication.CreateBuilder(args);
// 注册自定义服务
builder.Services.AddSingleton<DemoEventService>();
// 注册飞书Webhook服务
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration, "FeishuWebhook")
.AddHandler<DemoDepartmentEventHandler>()
.AddHandler<DemoDepartmentDeleteEventHandler>()
.AddHandler<DemoDepartmentUpdateEventHandler>()
.Build();
var app = builder.Build();
// 添加飞书Webhook中间件
app.UseFeishuWebhook();
app.Run();
高级示例
包含健康检查、性能监控和自定义端点:
using Mud.Feishu.Webhook.Extensions;
var builder = WebApplication.CreateBuilder(args);
// 配置日志
builder.Logging.AddConsole();
builder.Logging.SetMinimumLevel(LogLevel.Debug);
// 注册飞书Webhook服务(高级配置)
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.EnableHealthChecks() // 启用健康检查
.AddHandler<MessageReceiveEventHandler>()
.AddHandler<DepartmentCreatedEventHandler>()
.Build();
var app = builder.Build();
// 健康检查端点
app.MapHealthChecks("/health");
// 飞书Webhook中间件
app.UseFeishuWebhook();
app.Run();
Demo 项目完整示例
Demo 项目提供了完整的测试和诊断功能:
using Mud.Feishu.Webhook.Demo.Handlers;
using Mud.Feishu.Webhook.Demo.Services;
using Mud.Feishu.Webhook.Extensions;
using Mud.Feishu.Webhook.Demo;
var builder = WebApplication.CreateBuilder(args);
// 注册演示服务
builder.Services.AddSingleton<DemoEventService>();
// 注册飞书Webhook服务
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration, "FeishuWebhook")
.AddHandler<DemoDepartmentEventHandler>()
.AddHandler<DemoDepartmentDeleteEventHandler>()
.AddHandler<DemoDepartmentUpdateEventHandler>()
.Build();
var app = builder.Build();
// 添加诊断端点(仅开发环境)
if (app.Environment.IsDevelopment())
{
app.MapDiagnostics(); // GET /diagnostics/handlers
app.MapTestEndpoints(); // POST /test/capture 等
}
// 添加飞书Webhook中间件
app.UseFeishuWebhook();
app.Run();
快速参考
最常用的代码模式
// ✅ 推荐:从配置文件读取(多应用配置)
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.AddHandler<YourEventHandler>()
.Build();
// ✅ 代码配置(需在 Apps 中配置每个应用的 Token 和 Key)
builder.Services.CreateFeishuWebhookServiceBuilder(options => {
options.GlobalRoutePrefix = "feishu";
})
.AddHandler<YourEventHandler>()
.Build();
// ✅ 高级配置
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.EnableHealthChecks()
.AddHandler<Handler1>()
.AddHandler<Handler2>()
.Build();
// ✅ 多应用独立处理器和拦截器
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.AddHandler<App1Handler>("app1")
.AddInterceptor<App1LoggingInterceptor>("app1")
.AddHandler<App2Handler>("app2")
.Build();
// ✅ 自定义验证器和密钥提供程序
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
.UseSignatureValidator<MySignatureValidator>()
.UseEncryptKeyProvider<AzureKeyVaultEncryptKeyProvider>()
.AddHandler<YourEventHandler>()
.Build();
常用配置项速查
| 配置项 | 默认值 | 说明 |
|---|---|---|
GlobalRoutePrefix |
"feishu" |
全局路由前缀 |
VerificationToken |
- | 验证令牌(必填) |
EncryptKey |
- | 加密密钥(32字节) |
MaxConcurrentEvents |
10 |
最大并发事件数,支持热更新 |
EventHandlingTimeoutMs |
30000 |
事件处理超时(毫秒) |
EnableTokenBackgroundRefresh |
null |
令牌后台刷新覆盖(null=不干预) |
EnforceHeaderSignatureValidation |
true |
强制签名验证(生产环境必须启用) |
TimestampToleranceSeconds |
30 |
时间戳容错范围(秒) |
多应用模式
Mud.Feishu.Webhook 支持多应用模式,允许你为不同的飞书应用配置独立的路由、处理器和配置。
配置文件
{
"FeishuWebhook": {
"GlobalRoutePrefix": "feishu",
"Apps": {
"app1": {
"VerificationToken": "app1_verification_token",
"EncryptKey": "app1_encrypt_key_32_bytes_long"
},
"app2": {
"VerificationToken": "app2_verification_token",
"EncryptKey": "app2_encrypt_key_32_bytes_long"
}
}
}
}
注册代码
// 为不同应用注册独立的处理器和拦截器
builder.Services.CreateFeishuWebhookServiceBuilder(builder.Configuration)
// App1 处理器和拦截器
.AddHandler<App1DepartmentEventHandler>("app1")
.AddHandler<App1MessageEventHandler>("app1")
.AddInterceptor<App1LoggingInterceptor>("app1")
// App2 处理器和拦截器
.AddHandler<App2DepartmentEventHandler>("app2")
.AddHandler<App2MessageEventHandler>("app2")
.AddInterceptor<App2AuditInterceptor>("app2")
.Build();
var app = builder.Build();
// 使用多应用中间件(自动处理路由)
app.UseFeishuWebhook();
app.Run();
💡 说明:通过
AddHandler<T>(appKey)和AddInterceptor<T>(appKey)注册的处理器和拦截器仅对指定应用生效,不会注册到全局集合,避免跨应用泄漏。
路由映射
系统会自动将路由映射到对应的应用:
/feishu/app1→ App1 的处理器/feishu/app2→ App2 的处理器
应用隔离
每个应用完全隔离:
- ✅ 配置隔离:每个应用独立的
EncryptKey和VerificationToken - ✅ 处理器隔离:每个应用只能调用自己的处理器
- ✅ 拦截器隔离:每个应用只能调用自己的拦截器
- ✅ 路由隔离:不同的路由前缀,互不干扰
- ✅ 安全隔离:每个应用独立的安全验证
- ✅ 限流隔离:基于
(AppKey, IP)维度的独立限流
详细的多应用文档请参阅:Readme.MultiApp.md
🚀 立即开始使用飞书Webhook,构建稳定可靠的事件处理系统!
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.AspNetCore.Http (>= 2.3.9)
- Microsoft.AspNetCore.Http.Abstractions (>= 2.3.9)
- Microsoft.Extensions.Configuration.Binder (>= 8.0.2)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Diagnostics.HealthChecks (>= 8.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.3)
- Microsoft.Extensions.Options (>= 8.0.2)
- Mud.Feishu.Abstractions (>= 3.0.0)
- System.Text.Json (>= 10.0.9)
-
net10.0
- Mud.Feishu.Abstractions (>= 3.0.0)
-
net6.0
- Microsoft.Extensions.Configuration.Binder (>= 8.0.2)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Diagnostics.HealthChecks (>= 8.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.3)
- Microsoft.Extensions.Options (>= 8.0.2)
- Mud.Feishu.Abstractions (>= 3.0.0)
-
net8.0
- Microsoft.Extensions.Configuration.Binder (>= 10.0.9)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Diagnostics.HealthChecks (>= 10.0.9)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Options (>= 10.0.9)
- Mud.Feishu.Abstractions (>= 3.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.0.0 | 0 | 9/28/2026 |
| 3.0.0-rc3 | 135 | 9/24/2026 |
| 3.0.0-rc2 | 152 | 9/18/2026 |
| 3.0.0-rc1 | 190 | 7/11/2026 |
| 3.0.0-preview5 | 172 | 6/25/2026 |
| 3.0.0-preview4 | 109 | 6/3/2026 |
| 3.0.0-preview3 | 131 | 5/22/2026 |
| 3.0.0-preview2 | 152 | 5/11/2026 |
| 3.0.0-preview1 | 170 | 5/1/2026 |
| 2.1.6 | 158 | 7/21/2026 |
| 2.1.5 | 185 | 6/25/2026 |
| 2.1.4 | 168 | 6/3/2026 |
| 2.1.3 | 210 | 5/22/2026 |
| 2.1.2 | 161 | 5/12/2026 |
| 2.1.1 | 129 | 5/11/2026 |
| 2.1.0 | 143 | 5/1/2026 |
| 2.0.9 | 198 | 4/24/2026 |
| 2.0.8 | 189 | 4/12/2026 |
| 2.0.7 | 185 | 4/7/2026 |
| 2.0.6 | 142 | 4/5/2026 |