它的前身,可以追溯到一个叫 Ndf 的项目。从那时算起,已经过去了十多年。今天,它以 Vane.Dispatch 1.0.0 的名字正式发布。
序:十年磨一剑
做后端的人大抵都绕不开同一个问题:如何把"一个调用请求"干净地路由到"一段业务代码",并且把参数绑定、校验、过滤、异常这些横切关注点从业务里剥离出来。
十多年前,这个项目最早以 Ndf 的雏形出现,承载着同样的想法。中间几经搁置、重写,直到 .NET 跨入 net8.0 / net10.0 时代,AOT、裁剪、源生成成为 mainstream 诉求,那个老想法才终于找到了最贴合的形态。2026-09-06,Vane.Dispatch 1.0.0 作为首个正式开源版本发布(此前 Ndf 已在公司内部多个项目中长期生产使用)。
本文基于仓库中的全部源码与文档(README、developer-guide.md、三个示例、33 个测试文件)写成,力求每一个结论都能在代码里落地。
它是什么
Vane.Dispatch 是一个微内核:按特性([Route])把调用路由到控制器操作,做模型绑定,跑过滤器管线,并基于接口生成代理。它只依赖一个最小的 ServiceResolver 委托,与任何 DI 容器、任何通信层都零耦合。
你可以自带解析器(或任意 IServiceProvider)、自带控制器、自带线格式。库本体仅依赖 net8.0 BCL,双目标 net8.0;net10.0,所有公开类型统一归属 Vane.Dispatch 命名空间。
它的定位不是"又一个 Web 框架",而是对标 ASP.NET Core MVC 的语义,但不绑定 HTTP 的契约分发引擎。这句话是理解整个库设计取舍的钥匙。
核心设计原则
原则 含义
容器无关 唯一耦合点是 ServiceResolver 委托;用任意 IServiceProvider 包一层即可,无需引入额外容器
传输层无关 路由/绑定/过滤器/格式化全部在内存里完成;HTTP、gRPC、二进制协议都只是"把字节适配成 DispatchRequest"的宿主
AOT 友好 IsAotCompatible / IsTrimmable 已开启;反射入口均有 [RequiresUnreferencedCode] / [RequiresDynamicCode] / [DynamicallyAccessedMembers] 标注
同步快路径 + 按需异步 默认全同步、Task 无关的零分配快路径;仅在处理器真正做 I/O 时切到 DispatchAsync
五分钟跑通
using Vane.Dispatch;
[Route("math")]
public class MathController
{
public int Add(int a, int b) => a + b;
public int Square(int x) => x * x;
}
// 极简解析器:仅作演示,不引入任何 DI 容器
ServiceResolver resolver = type =>
type.GetConstructor(Type.EmptyTypes)?.Invoke(null)
?? throw new MissingMethodException(type.FullName);
var engine = resolver
.BuildDispatchEngine()
.MapDispatchable<MathController>()
.Build();
int sum = engine.Dispatch<int>("math", "Add", new { a = 2, b = 3 }); // 5
int sq = engine.Send<MathController, int>("Square", 6); // 36
public interface IMath { int Square(int x); }
var proxy = engine.CreateProxy<IMath>();
int px = proxy.Square(9); // 81
如果你已经有 IServiceProvider,直接 services.BuildDispatchEngine() —— BCL 适配器会把你的 provider 包成解析委托,无需引入 Vane.Container。
特性详解
-
特性路由(Attribute Routing)
类级 [Route] 同时承担三件事:可分发声明 + 路由组名 + 默认自动绑定推断(对标 MVC 的 Controller + [Route] + [ApiController])。方法级 [Route] 是操作契约名 / 别名。查表大小写不敏感,构建期做重复操作检测(fail-fast)。[NoAutoBind] 可在类级关闭自动绑定。 -
模型绑定:六种来源一次看懂
[FromBody] / [FromRoute] / [FromHeader] / [FromQuery] / [FromServices],外加具名参数与复杂对象递归绑定。[FromRoute]/[FromHeader]/[FromQuery] 严格来源隔离——[FromHeader] 即使路由值里存在同名键也不会命中。
[Route("binder")][NoAutoBind]
public sealed class BinderController
{
public byte[] EchoBody([FromBody] byte[] data) => data; // BodyModelBinder
public string Service([FromServices] IGreeter dep) => dep.Greet(); // ServicesModelBinder
public int QueryVal([FromQuery] int q) => q; // ValueProviderModelBinder
public int RouteVal([FromRoute] int id) => id;
}
默认 [Route] 还按类型自动推断:复杂 DTO → Body、接口 → Services、简单类型 → Payload,无需逐个写 [From*]。
- 过滤器管线(对标 MVC 三态 OnExecuted)
全局与操作级过滤器。OnExecuted 在成功 / 异常 / 取消三种状态下恒运行,上下文携带 Exception / Canceled / ExceptionHandled,与 MVC 的 OnActionExecuted 语义一致。IExceptionFilter 可置 ExceptionHandled 免除失败判定。
内置 UseValidation() 一键启用 DataAnnotations 校验(含 IValidatableObject),错误聚合到统一 ModelState → 400。默认零校验开销:不装配就不介入。
- 格式化器与接口代理
JSON、原始字节、扁平二进制三种内置输入/输出格式,可插入 MessagePack 等自定义二进制协议。
CreateProxy<T>() 把任意接口变成进程内强类型客户端,走同一套分发内核(路由/绑定/过滤器),热路径零反射。
- 自由端点(Minimal API 风格)
Map / MapGet / MapPost / Handle 注册委托端点,支持 {name} 路径段捕获:
var engine = resolver.BuildDispatchEngine()
.MapGet("/ping", () => "pong")
.MapPost("/echo", (string msg) => msg)
.Build();
-
异步与取消
DispatchAsync 复用同一同步内核(路由/绑定/同步过滤器原样同步),仅对 Task / ValueTask 处理器 await,并注入 CancellationToken。令牌取消统一映射为 499(RequestCanceled)。 -
错误模型
DispatchResponse 是唯一响应载体,内置 Ok(200) BadRequest(400) NotFound(404) RequestCanceled(499) InternalServerError(500)。ProblemDetails(RFC 7807)在成功时为 null(零分配),失败时惰性生成供跨进程/HTTP 宿主序列化。
性能与 AOT
路由表预构建,热路径分发用表达式编译调用器而非反射;同步路径目标零分配确定性延迟。
值提供器 / 绑定 / 路由是纯 CPU + 内存操作,刻意保持同步——没有可重叠的阻塞点,套 async 只增加状态机成本。
异步 invoker 惰性编译,纯同步应用不产生常驻异步委托成本。
库开启 XML 文档注释且未抑制 CS1591,构建保持 0 警告(TreatWarningsAsErrors 已开)。
对标 MVC,而不是复刻 HTTP
这是库里最值得说清的一点。作者没有把 MVC 搬过来,而是做了一份克制的差异清单(docs/MVC-Dispatch-Gap-Plan.md):
不做:[HttpGet]/[HttpPost] 动词约束、内容协商、IActionResult 体系、鉴权/资源/结果三类过滤器、IFormFile 表单绑定、{id:int} 路由类型约束。
原因:Vane 没有 HTTP,这些语义本就不存在;直接返回 POCO 反而是定位优势。
已等价具备:[ApiController] 自动 400、UseValidation() 批量校验、IExceptionFilter、结构化 ProblemDetails。
"对标是为了认知零障碍迁移,不是复刻 HTTP"——这条原则贯穿了整个 API 设计。
零传输耦合的真实证据
examples/Vane.Dispatch.HttpExample 用 System.Net.HttpListener(仅 BCL)把 HTTP 请求适配成 DispatchRequest,再经同一引擎完成绑定 + 过滤 + 表达式编译调用,响应经 IOutputFormatter 序列化。整个宿主只有约 150 行,没有引用任何第三方 HTTP 框架——这就是"传输层无关"不是口号的证明。
起步与获取
dotnet add package Vane.Dispatch
dotnet build Vane.Dispatch.slnx -c Release
dotnet test tests/Vane.Dispatch.Tests -c Release
源码与文档:https://gitee.com/netcasewqs/vane-dispatch
许可:MIT
作者:wangqingsong
结语
从 Ndf 到 Vane.Dispatch,变的是运行时的形态,不变的是"把分发逻辑做薄、做纯、做快"的执念。1.0.0 只是起点——它现在对标 MVC 语义、拥抱 AOT,下一步是把这条微内核路线在更多传输层上跑起来。
如果你也在做服务分发、RPC 网关、或想给一套老系统换一个轻量内核,欢迎试一试,也欢迎提 Issue。
https://coub.com/view/k9rgprwh5x
https://coub.com/view/rxs85ivz61
https://coub.com/view/cqimy5yyiu
https://coub.com/view/2hmmztfi4f
https://coub.com/view/wcztp6jbwu
https://coub.com/view/d88lc2say8
https://coub.com/view/9kxpu4mom4
https://coub.com/view/asht95kmhc
https://coub.com/view/2scyqp0ll5
https://coub.com/view/igoqas6thh
https://coub.com/view/9bgovrthya
https://coub.com/view/gtea43xrey
https://coub.com/view/e8qqqih3fq
https://coub.com/view/4ushrt51l5
https://coub.com/view/ggo0rlw2nu
https://coub.com/view/hak8u14ltv
https://coub.com/view/nmw897m682
https://coub.com/view/qk6c3k7snf
https://coub.com/view/5z7xboecnd
https://coub.com/view/49ho0wupu5
https://coub.com/view/m8ctbobl09
https://coub.com/view/fdkxqldobf
https://coub.com/view/cvfx8czfx5
https://coub.com/view/1o44sby5vd
https://coub.com/view/21kxd8lywa
https://coub.com/view/7ks5bvzlz4
https://coub.com/view/1rxfhhjqoy
https://coub.com/view/9bhc3rx5rv
https://coub.com/view/amzvv3zrjn
https://coub.com/view/hi0xznoimo
https://coub.com/view/rx9my028x1
https://coub.com/view/p5awfkuu0f
https://coub.com/view/xmh16z2jj8
https://coub.com/view/fmd0jus51s
https://coub.com/view/af0kz9488i
https://coub.com/view/om2p8x1vgg
https://coub.com/view/p2jq8k7v0r
https://coub.com/view/e61z21xbxe
https://coub.com/view/92x9e2b278
https://coub.com/view/poilyx0z3v
https://coub.com/view/3wub62du52
https://coub.com/view/rukz1tn3wl
https://coub.com/view/euf6pe0ppf
https://coub.com/view/o8h9too3ai
https://coub.com/view/rjgk1h076m
https://coub.com/view/2vgvwb4r7c
https://coub.com/view/qebpj0jxsb
https://coub.com/view/1bt3cgoam9
https://coub.com/view/37mm3cle2d
https://coub.com/view/h94xh8m3na
https://coub.com/view/erg2xa3kar
https://coub.com/view/5j5hgvp2na
https://coub.com/view/8kj86mlc3m
https://coub.com/view/7ic6g33n48
https://coub.com/view/pl1eryhc87
https://coub.com/view/kkeiizonyp
https://coub.com/view/sbbo1t5hse
https://coub.com/view/u1tn98dow0
https://coub.com/view/b1q6pqkeob
https://coub.com/view/yq3z1h92gk
https://coub.com/view/ygy213fnt2
https://coub.com/view/2b7zx0vp01
https://coub.com/view/2e7a99u43q
https://coub.com/view/1kv0a08lhd
https://coub.com/view/w8sfbcak30
https://coub.com/view/ggq802pof9
https://coub.com/view/qhoxbjzqj8
https://coub.com/view/i82ydbp3s8
https://coub.com/view/1orvbuspwg
https://coub.com/view/ejjgnta2cl
https://coub.com/view/a5j1sy2vqg
https://coub.com/view/vzecz1kn41
https://coub.com/view/ibjkvodol8
https://coub.com/view/7lihfdb2w0
https://coub.com/view/4izfxvvo5i
https://coub.com/view/iv4jz2frv0
https://coub.com/view/zjo7pzntwc
https://coub.com/view/hs4ol24nts
https://coub.com/view/ofwc2lcc4u
https://coub.com/view/cuniitponp
https://coub.com/view/adpw09n06l
https://coub.com/view/vrdxseoboo
https://coub.com/view/5c1i4b3473
https://coub.com/view/8o4tav9332
https://coub.com/view/yr35d283cs
https://coub.com/view/w332jfdd1o
https://coub.com/view/y3z45t3hqm
https://coub.com/view/60w44cxkab
https://coub.com/view/fdfkl4r30h
https://coub.com/view/ai4vq1fx29
https://coub.com/view/xrqh7i825e
https://coub.com/view/utomhptstw
https://coub.com/view/sg0crpwl0b
https://coub.com/view/0ispkmtoud
https://coub.com/view/wrp22niatw
https://coub.com/view/20bx0mbzmv
https://coub.com/view/60m4w5dcbq
https://coub.com/view/41hzdpnyke
https://coub.com/view/z6rrvjlxtz
https://coub.com/view/0hjsh1e169
https://coub.com/view/q2fonp61fu
https://coub.com/view/33nc9c9umk
https://coub.com/view/pva01y3akz
https://coub.com/view/ss2ajp2jme
https://coub.com/view/pel9x6k3rd
https://coub.com/view/pc9jgz8wed
https://coub.com/view/oe3it65gzz
https://coub.com/view/y3sd32e8gn
https://coub.com/view/pkghl6b8kg
0 个评论
要回复文章请先登录或注册