- 编程语言
- 编译器
- 云原生
- 语言运行时
【免费下载链接】wing
A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡
本篇技术指南以 Wing 仓库中 api_valid_path.test.w_test_sim.md 快照及其配套测试源码为核心,系统讲解cloud.Api定义 HTTP 路由时路径(path)的合法语法、底层校验实现、跨平台(sim / tf-aws / awscdk)的调用链,以及编译到 AWS 后 OpenAPI 与 Lambda 权限的生成方式。读完你将能准确判断一条 API 路径为何合法或非法,并写出可被 Wing 编译器接受的云 API 路由。
快照文档说明了什么
tools/hangar/__snapshots__/test_corpus/valid/api_valid_path.test.w_test_sim.md是 Hangar 测试框架在test目标、sim平台下生成的输出快照,原文内容如下:
pass ─ api_valid_path.test.wsim (no tests) Tests (0) Snapshots 1 skipped Test Files 1 passed (1) Duration <DURATION>这份快照有两点值得注意:
- 该用例以
pass通过:api_valid_path.test.w在 sim 平台下编译与运行均成功,说明它属于仓库中的“合法测试”(tests/valid/),而不是用于验证编译器报错行为的错误用例。 (no tests):测试文件本身没有声明test块,因此快照显示Tests (0)、Snapshots 1 skipped。路径合法性的验证全部发生在 preflight(预检)阶段——也就是程序在“构建基础设施”时通过同步抛错/捕获的方式完成,而非运行时行为。
快照顶部的标题链接指向真正的测试源码 tests/valid/api_valid_path.test.w,该文件才是这条路径校验规则的完整行为规格,下文将基于它展开。
cloud.Api 的路径语法规则
Wing 的cloud.Api(全限定名@winglang/sdk.cloud.Api,定义于 packages/@winglang/sdk/src/cloud/api.ts)通过api.get(path, inflight)、api.post(...)等方法注册路由。注册时传入的path必须满足严格语法约束,违反即抛出异常。
错误消息原文(来自 packages/@winglang/sdk/src/cloud/api.ts#L418-L426):
Invalid path {path}. Url parts can only contain alpha-numeric chars, "-", "_" and ".". Params can only contain alpha-numeric chars and "_".翻译并拆解规则:
- URL 静态段(Url parts):只能包含字母、数字、连字符
-、下划线_和点.; - 路径参数段(Params,以
:开头的段):只能包含字母、数字和下划线_,不能为空; - 路径必须以
/开头(/根路径本身合法),参数标记:只能出现在某个段的开头,不能出现在段中间; - 允许携带 query string(
?后跟非#字符),#被视作不合法字符。
测试用例全集:无效路径与有效路径对照
测试文件 tests/valid/api_valid_path.test.w 通过两个辅助函数完成全部断言:testInvalidPath捕获注册路由时的异常并与预期错误消息比对,testValidPath断言“无异常抛出”(错误为空字符串)。
17 个无效路径用例
testInvalidPath("/test/:sup{er/:annoying//path"); testInvalidPath("/test/{::another:annoying:path}"); testInvalidPath("/test/n0t_alphanumer1cPa*th"); testInvalidPath("/test/path/:with/:two{invali4d#/variables"); testInvalidPath("/test/path/:/empty"); testInvalidPath("/test/m:issplaced"); testInvalidPath("/test/misspla:/ced"); testInvalidPath("/:sup{er/{annoying//path}"); testInvalidPath("/:^^another^annoying^path"); testInvalidPath("/n0t_alphanumer1cPa{th"); testInvalidPath("/:with/:two}invali4d#/variables"); testInvalidPath("/m:issplaced"); testInvalidPath("/missplaced:"); testInvalidPath("test"); testInvalidPath("/:/empty"); testInvalidPath("/:"); testInvalidPath("/:no.dots.here");逐一归纳其违规点:
| 无效路径 | 违规原因 |
|---|---|
/test/:sup{er/:annoying//path | 含{、双斜杠// |
/test/{::another:annoying:path} | 含{、},参数名段为空 |
/test/n0t_alphanumer1cPa*th | 含* |
/test/path/:with/:two{invali4d#/variables | 含{、# |
/test/path/:/empty、/:/empty、/: | 参数名段为空(:后无内容) |
/test/m:issplaced、/m:issplaced、/test/misspla:/ced、/missplaced: | :出现在段中间,而非段首 |
/:^^another^annoying^path | 含^ |
/:with/:two}invali4d#/variables | 含}、# |
/n0t_alphanumer1cPa{th | 含{ |
test | 未以/开头 |
/:no.dots.here | 参数段中使用了.(Params 只允许字母数字与_) |
13 个有效路径用例
testValidPath("/test/path"); testValidPath("/test/alphanumer1cPa_th"); testValidPath("/test/regular/path"); testValidPath("/test/pa-th/:with/two/:variable_s/f?bla=5&b=6"); testValidPath("/test/param/is/:last"); testValidPath("/test/path/:param"); testValidPath("/:param"); testValidPath("/t/:param"); testValidPath("/test/regular/path/:param"); testValidPath("/test/segment1/:param1/segment2?query1=value1?query2=value2"); testValidPath("/test/segment1/segment2?query=value1&query2=value2"); testValidPath("/test/path.withDots"); testValidPath("/test/path/.withDots/:param/:param-dash/x"); testValidPath("/", new cloud.Api() as "api for root path");从有效用例可以确认的细节:
- 静态段可以混合字母数字与
-、_、.(如/test/pa-th、/test/path.withDots、/test/path/.withDots); - 参数段用
:param形式,参数名允许_(如:variable_s)但不能含.、-; - 可携带 query string,支持
&与多个?分隔(如?query1=value1?query2=value2也被接受); - 根路径
/完全合法——测试还特意为根路径单独创建了一个new cloud.Api() as "api for root path"实例来验证。
底层实现:_validatePath与正则剖析
路径校验并非分散在各平台代码中,而是集中在基类cloud.Api的一个受保护方法里。packages/@winglang/sdk/src/cloud/api.ts#L411-L426:
/** * Validating path: * if has `:` prefix - the part following that prefix is only letter, digit or _, not empty and placed before and after "/" * @param path * @throws if the path is invalid * @internal */ protected _validatePath(path: string) { if ( !/^((\/\:[a-zA-Z0-9_\-]+|\/[a-zA-Z0-9_\-\.]*)*(?:\?[^#]*)?)?$/g.test(path) ) { throw new Error( `Invalid path ${path}. Url parts can only contain alpha-numeric chars, "-", "_" and ".". Params can only contain alpha-numeric chars and "_".`, ); } }正则表达式逐段拆解:
\/\:[a-zA-Z0-9_\-]+:匹配参数段。以/开头、紧跟:,参数名由字母、数字、_、-组成且至少一个字符。注意注释与正则中\-的存在——但测试用例/:no.dots.here与/:param-dash说明:.withDots场景下参数实际仅允许字母数字与_,-与.只在静态段合法,因此该正则在语义上由错误消息与用例共同界定。\/[a-zA-Z0-9_\-\.]*:匹配静态段,字符集为字母、数字、_、-、.,可为空(对应空段或//场景在整体校验中会被拒绝)。- 整体以
(...)*(?:\?[^#]*)?结尾:允许零个或多个段,?后的 query 部分可含任意非#字符。 - 锚点
^...$配合g标志:要求整个字符串完全匹配,任何多余字符都会导致校验失败并抛出Error。
该方法被标注为@internal,意味着它不面向用户直接调用,而是由各平台实现通过公开的get/post/put/...入口在注册路由时隐式触发。
跨平台调用链:三个平台都在哪里校验
从源码结构看,_validatePath在三个目标平台的路由注册路径上被一致调用,构成“一处定义、处处生效”的校验架构:
- sim(本地模拟器):packages/@winglang/sdk/src/target-sim/api.ts#L123-L142 的私有方法
addEndpoint第一行即执行this._validatePath(path),随后才_addToSpec写入 OpenAPI 规范并创建处理函数。所有 HTTP 方法(GET/POST/PUT/DELETE 等)都收敛到这一个入口。 - tf-aws(Terraform + AWS):packages/@winglang/sdk/src/target-tf-aws/api.ts#L61-L83 的
httpRequests同样先this._validatePath(path),再通过addHandler创建 Lambda、this.api.addEndpoint写入 API Gateway 的 OpenAPI body。 - awscdk:packages/@winglang/platform-awscdk/src/api.ts#L52-L72 的
httpRequests结构一致,也是先校验、后注册。
这种设计保证:无论用户选择哪个部署目标,非法路径都会在 preflight 阶段被立即拒绝,而不是等到云端部署或运行时才暴露问题。这也解释了快照中(no tests)却能通过的原因——校验发生在编译期/合成期。
编译产物验证:tf-aws 快照中的 OpenAPI 与权限生成
api_valid_path.test.w_compile_tf-aws.md 记录了该测试在tf-aws目标下的合成结果,可以从生成物反证路径规则的落地形态:
- OpenAPI body:所有 13 个有效路径都出现在
aws_api_gateway_rest_api的 OpenAPI 3.0.3 body 中。带参数的路径(如/test/pa-th/{with}/two/{variable_s}/f?bla=5&b=6、/{param})会在parameters数组中生成{"name":"with","in":"path","required":true,"schema":{"type":"string"}}这样的路径参数声明。 - Lambda 函数收敛:多个路径共用同一个 inflight handler 时,只会生成一个 Lambda(如
defaultapi_get_test_path0_B7A07AB2),路径差异通过aws_lambda_permission的source_arn区分,形如.../execution_arn/*/GET/test/path/{param}。注意权限源 ARN 中参数仍以{param}形式(OpenAPI 风格)出现。 - 根路径 API:
api for root path实例对应GET/,其source_arn为.../*/GET/,确认根路径被完整支持。
此外快照中的preflight.cjs完整保留了测试逻辑:testInvalidPath/testValidPath被编译为闭包,所有 30 个路径调用按序执行,与 Wing 源码一一对应,可作为理解“Wing 代码 → preflight JS”映射的参考。
实战建议:如何编写合法且健壮的 API 路径
结合以上规则,在实际使用cloud.Api时遵循以下要点即可避免踩坑:
- 静态段统一使用小写字母、数字、
-、_,需要分隔单词时优先-(如/note/body);_与.同样被允许,但尽量保持一致风格。 - 参数段使用
:name形式且参数名只用字母数字与_;避免:my-param、:my.param这类写法——参数名中的.与-会导致校验失败(可对比测试中/:no.dots.here与有效的:param-dash所在静态段的区别)。 - 路径必须以
/开头,根路径"/"合法;不要写"test"这类不带前导斜杠的路径。 - 参数必须独占一个段,
m:issplaced、missplaced:这类写法非法;:只能在段首。 - 允许携带query string,如
api.get("/test/path?lang=wing&page=2", handler),#不得出现在 query 中。 - 运行时在 inflight handler 中通过
request.vars.get("name")读取路径参数、request.query.get("name")读取 query 参数——参见 docs/by-example/34-http-server.md 中api.put("/note/:name", ...)的用法示例。
小结
从一条 12 行的 hangar 快照出发,可以还原出 Wing 对cloud.Api路径校验的完整设计:错误消息明确定义字符白名单(静态段允许字母数字-_.,参数段仅允许字母数字_),底层由基类_validatePath的正则统一把关,sim、tf-aws、awscdk 三个平台在各自的路由注册入口一致调用,非法路径在 preflight 阶段即被拒绝。测试用例集(17 个非法 + 13 个合法路径)既是行为规格,也是可复用的校验清单。对读者而言,这份快照 + 测试 + 源码构成了理解 Wing 云 API 路由约束的最小闭环,可作为编写新 API 路由时的直接参考。
- 编程语言
- 编译器
- 云原生
- 语言运行时
【免费下载链接】wing
A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡
相关推荐
Wing 语言实战:从 Hangar 快照解读 cloud.Api GET 路由与 http 客户端调用测试
Wing 语言实战:从 Hangar 快照解读 cloud.Api GET 路由与 http 客户端调用测试 本篇文章以 wing 仓库中 Hangar 快照测
编程语言编译器云原生语言运行时Wing 云原生 Api 的 404 兜底路由:从 hangar 快照看 tf-aws 目标下的未定义路径处理
Wing 云原生 Api 的 404 兜底路由:从 hangar 快照看 tf aws 目标下的未定义路径处理 导读 本篇文章以 404.test.w 编译快照
编程语言编译器云原生语言运行时Wing 语言 math.log10 用法详解:从 SDK 实现到 Hangar 测试快照验证
Wing 语言 math.log10 用法详解:从 SDK 实现到 Hangar 测试快照验证 导读:本文围绕 Wing 标准库 math 模块中的 log10
编程语言编译器云原生语言运行时
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考