- 编程语言
- 编译器
- 云原生
- 语言运行时
【免费下载链接】wing
A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡
cloud.Api是 Wing 标准库(cloud命名空间)中用于创建云端 HTTP 端点的内置资源,它把"路径 + HTTP 方法 + inflight 事件处理函数"这一路由模型统一封装成跨云抽象。本篇指南以 cloud/api.md 的官方 API 参考为骨架,结合仓库中 SDK 源码(packages/@winglang/sdk/src/cloud/api.ts)与模拟器、AWS 实现,带读者从零构建一个完整的 REST API,理解路由注册、请求/响应对象、CORS 配置,并深入剖析 OpenAPI Spec 生成、路径校验等底层原理,最终掌握在 Wing 中编写可移植 HTTP 服务的完整实战方案。
一、cloud.Api是什么:用一条语句定义 HTTP 端点
cloud.Api资源代表一组可以由互联网客户端调用的 HTTP 端点集合。在 Wing 的应用模型中,API 通常充当应用的前门:客户端通过它访问后端服务中的数据、业务逻辑或功能。
它的核心建模方式是:端点(endpoint)= 一组路由(route),每个路由绑定一个事件处理函数(event handler function)。一条路由由两部分组成:
- 一个路径(path),例如
"/users/:userid",其中:userid是路径参数; - 一组 HTTP 方法(method),例如
GET、POST或DELETE。
当客户端调用某条路由时,对应的事件处理函数(一个 inflight 闭包)就会执行。从 cloud/api.ts 中的抽象基类 可以看出,Api继承自Resource,构造函数接受可选的ApiProps(目前仅含 CORS 相关配置),并在构造树节点时设置标题为"Api"、描述为"A REST API endpoint"。
这种设计带来的直接收益是:开发者只需要用语言层面的api.get(...)、api.post(...)等 preflight 方法声明路由,Wing 编译器与 SDK 会负责把路由翻译成目标云平台的产物(AWS API Gateway、模拟器中的 Express 服务器等),无需关心各平台底层差异。
二、快速上手:一个完整的 REST API 示例
原文档给出了一个使用cloud.Api、cloud.Bucket与cloud.Counter协同实现的员工管理 REST API。以下代码完整复现并逐步解读:
bring cloud; let api = new cloud.Api(); // Used for generating unique id let counter = new cloud.Counter(); // our employee data let store = new cloud.Bucket(); api.get("/employees", inflight (request: cloud.ApiRequest): cloud.ApiResponse => { let result = MutJson []; let var i = 0; for employee in store.list() { result.setAt(i, employee); i = i + 1; } return cloud.ApiResponse { status: 200, body: Json.stringify(result) }; }); api.get("/employees/:id", inflight (request: cloud.ApiRequest): cloud.ApiResponse => { let employee = store.get(request.vars.get("id")); return cloud.ApiResponse { status: 200, body: Json.stringify(employee) }; }); api.post("/employees", inflight (request: cloud.ApiRequest): cloud.ApiResponse => { if let body = request.body { let employeeData = Json.parse(body); let id = "{counter.inc()}"; store.putJson(id, employeeData); return cloud.ApiResponse { status: 201, body: id }; } }); api.put("/employees/:id", inflight (request: cloud.ApiRequest): cloud.ApiResponse => { if let body = request.body { let employeeData = Json.parse(body); let id = request.vars.get("id"); store.putJson(id, employeeData); return cloud.ApiResponse { status: 200, body: Json.stringify(employeeData) }; } }); api.delete("/employees/:id", inflight (request: cloud.ApiRequest): cloud.ApiResponse => { let id = request.vars.get("id"); store.delete(id); return cloud.ApiResponse { status: 204 }; });逐段要点:
- GET 列表:
api.get("/employees", ...)遍历store.list()返回的键集合,把结果组装进MutJson数组,最后以status: 200和Json.stringify(result)返回 JSON 字符串。注意这里返回体必须是字符串,因此所有结构化数据都通过Json.stringify序列化。 - GET 单个:路径参数通过
request.vars.get("id")读取(vars是MutMap<str>,由路由中:id部分注入),随后从 Bucket 取出对象并返回。 - POST 创建:从
request.body读取请求体,用Json.parse(body)解析为 JSON,借助counter.inc()生成唯一自增 id,store.putJson(id, employeeData)写入存储,最后返回201与 id 字符串。 - PUT 更新:从
request.vars.get("id")定位资源,覆盖写入后返回200与新数据。 - DELETE 删除:按 id 删除对象,返回
204 No Content(无响应体)。
从源码看这条调用链如何工作
在模拟器(sim)实现中,target-sim/api.ts 的addEndpoint是路由注册的统一入口:
- 先调用
_validatePath(path)校验路径合法性; - 调用
_addToSpec(...)把路由登记到内部的 OpenAPI Spec 中; - 通过
createOrGetFunction将 inflight 处理函数包装成Function资源,并建立EventMapping事件映射; - 向
Policy添加一条FunctionInflightMethods.INVOKE权限语句,确保 API 有权调用处理函数。
也就是说,每注册一条路由,SDK 都会在后台为你创建"函数资源 + 事件订阅 + 权限策略"这整套基础设施,这正是 Wing"基础设施即代码、代码即基础设施"统一模型的体现。
三、深入理解请求对象与响应对象
ApiRequest:inflight 处理函数的入参
ApiRequest是传入 inflight handler 的请求形状,字段如下(与 源码定义 一致):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
method | HttpMethod | 是 | 请求的 HTTP 方法 |
path | str | 是 | 请求路径 |
query | MutMap<str> | 是 | 查询字符串(query string)键值 |
vars | MutMap<str> | 是 | 路径变量(path variables),如:id匹配到的值 |
body | str | 否 | 请求体 |
headers | MutMap<str> | 否 | 请求头 |
在模拟器中,这个对象由 api.inflight.ts 的transformRequest从 Express 的req转换而来:headers与query都经过sanitizeParamLikeObject处理(把多值参数用逗号拼接成单值,见 cloud/api.ts),method通过parseHttpMethod转成枚举,vars直接取 Express 的req.params。
ApiResponse:inflight 处理函数的返回值
ApiResponse是 inflight handler 的响应形状:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
status | num | 200 | 响应状态码 |
body | str | 无 body | 响应体 |
headers | MutMap<str> | {} | 响应头 |
源码中定义了DEFAULT_RESPONSE_STATUS = 200(见 cloud/api.ts)。模拟器在收到 handler 返回后按此逻辑处理(api.inflight.ts):状态码缺省用 200,逐项设置响应头,body存在则res.send(body),否则res.end()。
处理函数契约:IApiEndpointHandler协议
任何可以传给api.get(...)等 preflight 方法的处理函数,都实现了IApiEndpointHandler协议(源码):它是一个带有 inflighthandle(request: ApiRequest): ApiResponse?方法的资源。这意味着你既可以传一个匿名 inflight 闭包,也可以传一个实现了该接口的自定义 inflight 类——SDK 对两者一视同仁。
四、目标平台实现细节(Target-specific Details)
cloud.Api是可移植抽象,同样的 Wing 代码可以编译到不同平台,底层产物各不相同:
模拟器(sim)
模拟实现基于 Node.js 的Express框架。api.inflight.ts 中可以看到:
- 使用
express()创建应用,并express.text({ limit: "10mb", type: "*/*" })把请求体统一按文本解析(10MB 限制与 AWS API Gateway 的 payload 上限对齐),解析工作留给 inflight handler 自己完成; - 每个路由通过
this.appmethod, asyncMiddleware(...))注册到 Express; transformRoutePath会把 OpenAPI 风格的{id}转回 Express 的:id写法(api.inflight.ts);- 因为 Express v4 不原生支持异步请求处理,SDK 用
asyncMiddleware包装异步 handler(api.inflight.ts); - 服务器启动后,
url形如http://localhost:<port>,即Api.url属性的值(api.inflight.ts)。
另外,模拟器会把上次使用的端口写入状态文件state.json,下次启动时若端口仍可用则优先复用,提升本地开发体验。
AWS(tf-aws与awscdk)
AWS 实现基于Amazon API Gateway。从 target-tf-aws/api.ts 可以看到 Terraform 版实现会生成ApiGatewayRestApi、ApiGatewayDeployment、ApiGatewayStage等基础设施资源;shared-aws/api.ts 定义了IAwsApi接口,暴露restApiArn、restApiId、restApiName、stageName、invokeUrl、deploymentId等 AWS 专属属性,便于在需要时下钻操作。
一个值得注意的细节:AWS 阶段名(stage name)固定为prod(shared-aws/api.ts),它会出现在 API 的 URL 中。此外,AWS 版在onLift时会通过环境变量把Api.url注入 inflight host(shared-aws/api.ts),保证 inflight 代码中访问 API 地址时用的是运行时环境变量而非硬编码。
Azure(tf-azure)与 GCP(tf-gcp)
截至本文档所依据的版本,Azure 与 GCP 平台尚不支持cloud.Api,原文档以 🚧 标注并链接到对应的跟踪 issue(Azure:winglang/wing#625;GCP:winglang/wing#624)。因此当前可用的目标是sim、tf-aws与awscdk。
五、ApiProps:开启 CORS 跨域配置
ApiProps是new cloud.Api(props?)的构造选项,目前只有两个字段,全部围绕 CORS 行为:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
cors | bool | false(CORS 配置关闭) | 设为true时按默认选项为所有路由添加 CORS 头 |
corsOptions | ApiCorsOptions | cors为true时应用默认 CORS 选项 | 自定义 CORS 细节 |
当cors: true但未传corsOptions时,默认选项为:
new cloud.Api({ cors: true, // 默认效果等价于: // allowOrigin: "*", // allowMethods: [HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, // HttpMethod.DELETE, HttpMethod.HEAD, HttpMethod.OPTIONS], // allowHeaders: ["Content-Type", "Authorization"], // exposeHeaders: [], // allowCredentials: false, });个性化示例:
new cloud.Api({ cors: true, corsOptions: { allowOrigin: "https://example.com" } });从 源码构造函数 可以看到:this.corsOptions = props.cors ? this._cors(props.corsOptions) : undefined;,而_cors方法(cloud/api.ts)用"默认值 + 用户覆盖"的方式合并配置,保证用户传入的字段优先。
ApiCorsOptions全参数详解
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
allowCredentials | bool | false | 是否允许携带凭据 |
allowHeaders | MutArray<str> | ["Content-Type", "Authorization"] | 允许的请求头列表 |
allowMethods | MutArray<HttpMethod> | GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS | 允许的 HTTP 方法列表 |
allowOrigin | str | "*" | 允许的来源(origin) |
exposeHeaders | MutArray<str> | [] | 暴露给客户端的响应头列表 |
maxAge | duration | 300 seconds | 浏览器缓存预检(preflight)结果的时长 |
这些选项最终通过静态函数cloud.Api.renderCorsHeaders(corsOptions)(源码实现)渲染成两组 HTTP 头:
defaultResponse(默认响应头):Access-Control-Allow-Origin、Access-Control-Expose-Headers、Access-Control-Allow-Credentials;optionsResponse(预检响应头):Access-Control-Allow-Origin、Access-Control-Allow-Headers、Access-Control-Allow-Methods、Access-Control-Max-Age。
在模拟器中,CORS 中间件会在收到OPTIONS请求时直接返回204并携带optionsResponse头,其他请求则附加defaultResponse头后继续(api.inflight.ts)。
说明:原文档注明,CORS 选项"可以在单条路由上覆盖"这一能力"尚未实现(not yet implemented)",当前只能做 API 级别的全局配置。
六、路由级选项:端点选项(Endpoint Options)
8 个路由方法(get/post/put/delete/patch/options/head/connect)的第三个可选参数props分别对应ApiGetOptions、ApiPostOptions、ApiPutOptions、ApiDeleteOptions、ApiPatchOptions、ApiOptionsOptions、ApiHeadOptions、ApiConnectOptions。这些类型都继承自共同的基类ApiEndpointOptions(源码),而ApiEndpointOptions本质上是FunctionProps的别名——也就是说,每个路由背后都是一个云函数,路由级选项就是函数级配置。
各路由级选项的完整字段如下:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
concurrency | num | 平台相关限制(模拟器为 100) | 同一时间最大并发调用数 |
env | MutMap<str> | 无环境变量 | 传给处理函数的环境变量 |
logRetentionDays | num | 30 | 函数日志保留天数;设置为负值表示日志永不过期 |
memory | num | 1024 | 分配给函数的内存大小(MB) |
timeout | duration | 1m | 函数最大运行时长 |
典型用法示例:
api.get("/slow", inflight (request: cloud.ApiRequest): cloud.ApiResponse => { // 长时间运算... return cloud.ApiResponse { status: 200, body: "done" }; }, { timeout: 5m, memory: 2048, concurrency: 20, env: { "REGION": "us-east-1" }, logRetentionDays: 7 });七、方法参考:8 种 HTTP 方法路由注册
Api提供 8 个 preflight 方法,签名完全一致,只是 HTTP 方法与选项类型不同:
| 方法 | 签名 | 说明 |
|---|---|---|
connect | connect(path, inflight, props?: ApiConnectOptions): void | 为指定路径注册 CONNECT 请求处理 |
delete | delete(path, inflight, props?: ApiDeleteOptions): void | 注册 DELETE 处理 |
get | get(path, inflight, props?: ApiGetOptions): void | 注册 GET 处理 |
head | head(path, inflight, props?: ApiHeadOptions): void | 注册 HEAD 处理 |
options | options(path, inflight, props?: ApiOptionsOptions): void | 注册 OPTIONS 处理 |
patch | patch(path, inflight, props?: ApiPatchOptions): void | 注册 PATCH 处理 |
post | post(path, inflight, props?: ApiPostOptions): void | 注册 POST 处理 |
put | put(path, inflight, props?: ApiPutOptions): void | 注册 PUT 处理 |
每个方法都在抽象基类中声明(cloud/api.ts),由各平台实现类落实(如 target-sim/api.ts 中每个方法都会调用addEndpoint)。
静态函数
| 静态函数 | 签名 | 说明 |
|---|---|---|
onLiftType | onLiftType(host: IInflightHost, ops: MutArray<str>) | Wing 编译器为每个需要在 inflight 使用本类型的 host 调用一次的钩子,常用于添加权限、环境变量等能力 |
toInflight | toInflight(obj: IResource) | 生成一段异步 JavaScript 语句,用于为资源创建 inflight client(必须在 async 上下文中执行) |
renderCorsHeaders | renderCorsHeaders(corsOptions?: ApiCorsOptions) | 生成包含默认 CORS 响应头与 OPTIONS 响应头的对象(返回CorsHeaders,结构含defaultResponse与optionsResponse两个MutMap<str>) |
renderOpenApiPath | renderOpenApiPath(path: str) | 把:风格的路径参数转换为合法的 OpenAPI 路径(/users/:id→/users/{id}) |
renderOpenApiPath的实现非常直观(cloud/api.ts):path.replace(/\/:([A-Za-z0-9_-]+)/g, "/{$1}"),它被_getOpenApiSpec用于把内部路径批量转成 OpenAPI 格式。
属性
| 属性 | 类型 | 说明 |
|---|---|---|
node | constructs.Node | 构造树节点 |
url | str | API 端点的基础 URL |
url的取值在模拟器下形如http://localhost:<port>,在 AWS 下则是 API Gateway 的调用地址(invokeUrl)。部署后把这个 URL 暴露给前端或写入环境变量,即可让客户端访问你的 API。
八、协议与枚举
HttpMethod枚举
端点的合法 HTTP 方法,共 8 个成员(源码):GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、PATCH。字符串与枚举互转由parseHttpMethod负责,遇到不支持的方法会抛出Unsupported HTTP method错误。
协议接口
IApiEndpointHandler:拥有 inflighthandle方法、可传给Api各 preflight 请求方法的资源,扩展自IInflight;IApiEndpointHandlerClient:IApiEndpointHandler的 inflight client,其handle(request: ApiRequest): ApiResponse?是真正在端点被请求时执行的入口。
九、源码级原理:路径校验、歧义检测与 OpenAPI Spec 生成
cloud.Api在注册路由时并非简单透传,抽象基类内置了一套严谨的路径治理逻辑,这可以从 cloud/api.ts 中看到:
- 路径格式校验(
_validatePath):路径段只允许字母数字以及-、_、.,参数段只允许字母数字和_;不满足则抛出Invalid path ...错误。这是路由注册前的第一道防线。 - 歧义检测(
_arePathsAmbiguous/_findAmbiguousPath):如果新路由与已存在路由的路径段数相同、且对应位置一个是参数一个是字面量(例如已有/api/hello/:name再注册/api/hello/foo的同一方法),会抛出is ambiguous错误。模拟器测试(target-sim/api.test.ts)专门验证了这类冲突场景。 - 兄弟路径检测(
_arePathsSiblings/_findSiblingPath):形如/:username与/:id这类"参数名不同但结构相同"的路径会被视为冲突,提示"try to match the parameter names to avoid this error"。 - 重复端点检测:同一路径同一方法重复注册会直接报
Endpoint for path '...' and method '...' already exists。 - OpenAPI Spec 生成(
_addToSpec/_getOpenApiSpec):每个路由被写入内部apiSpec,生成operationId(如get-employees)、路径参数定义、200响应描述与 CORS 头 schema;最终_getOpenApiSpec返回符合OpenAPI 3.0.3规范的完整 spec(cloud/api.ts),并调用renderOpenApiPath将路径参数统一转换为{id}格式。这份 spec 正是各平台生成 API Gateway / Express 路由的蓝图。
十、测试验证:如何在仓库中验证这些行为
如果你想在仓库中亲自验证上述行为,可以关注以下测试与源码路径:
- 模拟器 API 行为测试:target-sim/api.test.ts,覆盖重复端点、歧义路径、请求回显(echo)、查询参数、路径参数等场景;
- Terraform-AWS 目标测试:target-tf-aws/api.test.ts,验证 API Gateway 产物;
- 请求处理函数测试:shared-aws/api.onrequest.inflight.test.ts。
此外,仓库中还有大量 Wing 语言层面的 API 用例,例如 tests/sdk_tests/api 下的.w文件,可以直接用 Wing CLI 在sim平台上运行,观察 Express 服务器如何处理请求与 CORS 头。
总结
cloud.Api是 Wing 云端 HTTP 服务的统一入口:一条路由声明即完成"路径匹配 + 函数创建 + 权限授予 + OpenAPI 记录"的全部工作。通过本文你可以:
- 使用
api.get/post/put/delete/...快速搭建 REST API,并借助ApiRequest的vars、query、body处理各种请求; - 通过
ApiProps.cors/corsOptions精确控制跨域策略; - 用路由级选项(
timeout、memory、concurrency、env、logRetentionDays)调优每个端点的底层函数; - 理解同一份代码在模拟器(Express)与 AWS(API Gateway)上的不同落地方式,以及 Azure/GCP 当前的未支持状态。
更多细节可继续查阅 cloud 标准库 API 文档 及 SDK 源码。
- 编程语言
- 编译器
- 云原生
- 语言运行时
【免费下载链接】wing
A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡
相关推荐
Wing 标准库 cloud.Api 完全指南:用一条 `Api` 资源构建跨云 HTTP/REST 端点
Wing 标准库 cloud.Api 完全指南:用一条 Api 资源构建跨云 HTTP/REST 端点 cloud.Api 是 Wing 标准库( @wingl
编程语言编译器云原生语言运行时Wing 云原生语言 `cloud.Api` CORS 配置全解析:从 `cors.test.w` 测试用例到多平台实现
Wing 云原生语言 cloud.Api CORS 配置全解析:从 cors.test.w 测试用例到多平台实现 导读 本文围绕 wing 仓库中 cors.t
编程语言编译器云原生语言运行时Wing 语言 cloud.Api 路径变量(Path Variables)机制详解:从路由定义到多目标平台实现
Wing 语言 cloud.Api 路径变量(Path Variables)机制详解:从路由定义到多目标平台实现 导读 在 Wing 语言中, cloud.Ap
编程语言编译器云原生语言运行时
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考