☰
Wing 标准库 `cloud.Api` 完全指南:从 REST API 实战到多平台实现原理
2026/10/13 1:50:13 网站建设 项目流程
  • 编程语言
  • 编译器
  • 云原生
  • 语言运行时

【免费下载链接】wing

A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡

项目地址:https://gitcode.com/gh_mirrors/wi/wing
点击查看免费下载

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是路由注册的统一入口:

  1. 先调用_validatePath(path)校验路径合法性;
  2. 调用_addToSpec(...)把路由登记到内部的 OpenAPI Spec 中;
  3. 通过createOrGetFunction将 inflight 处理函数包装成Function资源,并建立EventMapping事件映射;
  4. 向Policy添加一条FunctionInflightMethods.INVOKE权限语句,确保 API 有权调用处理函数。

也就是说,每注册一条路由,SDK 都会在后台为你创建"函数资源 + 事件订阅 + 权限策略"这整套基础设施,这正是 Wing"基础设施即代码、代码即基础设施"统一模型的体现。

三、深入理解请求对象与响应对象

ApiRequest:inflight 处理函数的入参

ApiRequest是传入 inflight handler 的请求形状,字段如下(与 源码定义 一致):

字段类型必填说明
methodHttpMethod是请求的 HTTP 方法
pathstr是请求路径
queryMutMap<str>是查询字符串(query string)键值
varsMutMap<str>是路径变量(path variables),如:id匹配到的值
bodystr否请求体
headersMutMap<str>否请求头

在模拟器中,这个对象由 api.inflight.ts 的transformRequest从 Express 的req转换而来:headers与query都经过sanitizeParamLikeObject处理(把多值参数用逗号拼接成单值,见 cloud/api.ts),method通过parseHttpMethod转成枚举,vars直接取 Express 的req.params。

ApiResponse:inflight 处理函数的返回值

ApiResponse是 inflight handler 的响应形状:

字段类型默认值说明
statusnum200响应状态码
bodystr无 body响应体
headersMutMap<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 行为:

属性类型默认值说明
corsboolfalse(CORS 配置关闭)设为true时按默认选项为所有路由添加 CORS 头
corsOptionsApiCorsOptionscors为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全参数详解

参数类型默认值说明
allowCredentialsboolfalse是否允许携带凭据
allowHeadersMutArray<str>["Content-Type", "Authorization"]允许的请求头列表
allowMethodsMutArray<HttpMethod>GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS允许的 HTTP 方法列表
allowOriginstr"*"允许的来源(origin)
exposeHeadersMutArray<str>[]暴露给客户端的响应头列表
maxAgeduration300 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的别名——也就是说,每个路由背后都是一个云函数,路由级选项就是函数级配置。

各路由级选项的完整字段如下:

参数类型默认值说明
concurrencynum平台相关限制(模拟器为 100)同一时间最大并发调用数
envMutMap<str>无环境变量传给处理函数的环境变量
logRetentionDaysnum30函数日志保留天数;设置为负值表示日志永不过期
memorynum1024分配给函数的内存大小(MB)
timeoutduration1m函数最大运行时长

典型用法示例:

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 方法与选项类型不同:

方法签名说明
connectconnect(path, inflight, props?: ApiConnectOptions): void为指定路径注册 CONNECT 请求处理
deletedelete(path, inflight, props?: ApiDeleteOptions): void注册 DELETE 处理
getget(path, inflight, props?: ApiGetOptions): void注册 GET 处理
headhead(path, inflight, props?: ApiHeadOptions): void注册 HEAD 处理
optionsoptions(path, inflight, props?: ApiOptionsOptions): void注册 OPTIONS 处理
patchpatch(path, inflight, props?: ApiPatchOptions): void注册 PATCH 处理
postpost(path, inflight, props?: ApiPostOptions): void注册 POST 处理
putput(path, inflight, props?: ApiPutOptions): void注册 PUT 处理

每个方法都在抽象基类中声明(cloud/api.ts),由各平台实现类落实(如 target-sim/api.ts 中每个方法都会调用addEndpoint)。

静态函数

静态函数签名说明
onLiftTypeonLiftType(host: IInflightHost, ops: MutArray<str>)Wing 编译器为每个需要在 inflight 使用本类型的 host 调用一次的钩子,常用于添加权限、环境变量等能力
toInflighttoInflight(obj: IResource)生成一段异步 JavaScript 语句,用于为资源创建 inflight client(必须在 async 上下文中执行)
renderCorsHeadersrenderCorsHeaders(corsOptions?: ApiCorsOptions)生成包含默认 CORS 响应头与 OPTIONS 响应头的对象(返回CorsHeaders,结构含defaultResponse与optionsResponse两个MutMap<str>)
renderOpenApiPathrenderOpenApiPath(path: str)把:风格的路径参数转换为合法的 OpenAPI 路径(/users/:id→/users/{id})

renderOpenApiPath的实现非常直观(cloud/api.ts):path.replace(/\/:([A-Za-z0-9_-]+)/g, "/{$1}"),它被_getOpenApiSpec用于把内部路径批量转成 OpenAPI 格式。

属性

属性类型说明
nodeconstructs.Node构造树节点
urlstrAPI 端点的基础 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 中看到:

  1. 路径格式校验(_validatePath):路径段只允许字母数字以及-、_、.,参数段只允许字母数字和_;不满足则抛出Invalid path ...错误。这是路由注册前的第一道防线。
  2. 歧义检测(_arePathsAmbiguous/_findAmbiguousPath):如果新路由与已存在路由的路径段数相同、且对应位置一个是参数一个是字面量(例如已有/api/hello/:name再注册/api/hello/foo的同一方法),会抛出is ambiguous错误。模拟器测试(target-sim/api.test.ts)专门验证了这类冲突场景。
  3. 兄弟路径检测(_arePathsSiblings/_findSiblingPath):形如/:username与/:id这类"参数名不同但结构相同"的路径会被视为冲突,提示"try to match the parameter names to avoid this error"。
  4. 重复端点检测:同一路径同一方法重复注册会直接报Endpoint for path '...' and method '...' already exists。
  5. 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 ⚡

项目地址:https://gitcode.com/gh_mirrors/wi/wing
点击查看免费下载

相关推荐

上一篇:Python调试不再难:VS Code Recipes提供的5种调试方法
下一篇:Textractor:游戏文本提取神器,3步实现外语游戏无障碍游玩

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询