TypeSpec 标准库分页模式完全指南:从 `@list` 到客户端/服务端驱动分页
TypeSpec 标准库分页模式完全指南从list到客户端/服务端驱动分页【免费下载链接】typespec项目地址: https://gitcode.com/GitHub_Trending/ty/typespecTypeSpec 编译器内置了一整套分页Pagination模式的原生支持通过list、pageItems、offset、nextLink、continuationToken等装饰器即可声明列表类操作无需为每种分页风格手写重复的模型。本文以 pagination.md 为骨架结合编译器源码与测试用例系统讲解客户端驱动、服务端驱动以及二者组合的分页建模方法并揭示这些装饰器在编译器内部如何被校验、收集与暴露给下游发射器。分页基础list与pageItems在 TypeSpec 中启用分页的第一步是两步组合用list装饰操作Operation并让操作返回类型中至少包含一个被pageItems装饰的属性。pageItems指向的必须是数组类型它表示本页返回的元素集合。list op listPets(): { pageItems pets: Pet[]; };在 decorators.tsp 中可以看到这两个装饰器的正式声明extern dec list(target: Operation)把操作标记为返回分页列表的 list 操作extern dec pageItems(target: ModelProperty)指明包含分页元素的数组属性。源码层面的约定更加严格。在 paging.ts 中pageItemsDecorator的校验逻辑会检查目标属性类型是否确实为数组isArrayModelType否则报decorator-wrong-target诊断而getPagingOperationpaging.ts会强制要求返回类型中存在pageItems属性缺失时产生missing-paging-items错误。对应测试见 paging.test.ts。从编译器视角看list只是开启分页语义检查的开关validatePagingOperations会遍历程序中的每个操作仅对标记了list的操作调用validatePagingOperation做深度校验paging.ts。客户端驱动分页偏移量、页大小与页索引客户端驱动Client driven pagination模式下分页状态由客户端自行维护客户端负责计算下一页、上一页应该请求什么参数。TypeSpec 提供 3 个装饰器标注操作参数装饰器含义类型要求pageSize每页返回的条目数量数值类型numericoffset跳过的条目数量数值类型numericpageIndex页索引数值类型numericoffset与pageIndex并非互斥但用途相同——都是告诉服务器从哪一页/第几条开始取。两者的区别在于偏移语义offset基于条数跳过pageIndex基于页序号。从源码看这三个装饰器都由createMarkerDecorator结合createNumericValidation生成paging.tscreateNumericValidation会通过isNumericType强制校验属性类型必须是数值例如int32、int64、float64等。示例 1固定页大小 偏移量list op listPets(offset skip?: int32 0): { pageItems pets: Pet[]; };skip默认值为0即默认从第一条开始。示例 2自定义页大小 偏移量list op listPets(offset skip?: int32, pageSize perPage?: int32 100): { pageItems pets: Pet[]; };perPage默认100客户端可通过传入不同值覆盖默认页大小。示例 3自定义页大小 页索引list op listPets(pageIndex page?: int32 1, pageSize perPage?: int32 100): { pageItems pets: Pet[]; };与offset的跳过条数不同这里的page表示从第 1 页开始逐页翻取。服务端驱动分页链接与续传令牌服务端驱动Server driven pagination模式下服务器在响应中返回导航信息客户端无需自行推算页码只需跟随服务器给出的指引。TypeSpec 提供 5 个装饰器标注响应属性装饰器含义典型位置nextLink指向下一页的链接响应体prevLink指向上一页的链接响应体firstLink指向第一页的链接响应体lastLink指向最后一页的链接响应体continuationToken续传令牌必须同时标注在请求参数和响应属性上请求参数 响应体关于continuationToken有一个容易忽略的约束它必须成对出现。请求参数上的continuationToken标记用哪个参数传递下一个令牌响应中的continuationToken标记哪个属性携带服务器签发的下一个令牌。该要求在 decorators.tsp 的注释中明确说明It MUST be specified both on the request parameter and the response.。注服务端驱动分页完全可以叠加在客户端驱动分页之上——例如既有pageIndex/pageSize参数又返回nextLink/prevLink链接。示例 1HTTP 服务使用续传令牌list op listPets(query continuationToken token?: string): { pageItems pets: Pet[]; continuationToken nextToken?: string; };此例中服务器响应的 body 内有一个nextToken属性作为续传令牌。客户端取下一页时把令牌作为 query 参数token传给下一个请求。续传令牌也可以放在其他位置例如 HTTP 响应头。下面的 spec 表明服务器可在响应头返回续传令牌客户端请求下一页时仍通过 query 参数传递list op listPets(query continuationToken token?: string): { pageItems pets: Pet[]; continuationToken header nextToken?: string; };测试用例还证实continuationToken的属性类型可以放宽为可空string | null、可选token?: string、可空可选token?: string | null甚至非字符串类型int32编译器均不报错paging.test.ts。示例 2HTTP 服务使用链接list op listPets(): { pageItems pets: Pet[]; links: { nextLink next?: url; prevLink prev?: url; firstLink first?: url; lastLink last?: url; }; };注意对于 HTTP 服务nextLink以及prevLink、firstLink、lastLink默认应通过 GET 请求跟随。链接 URI 应被视作不透明 URL其中已包含导航到对应页面所需的全部信息。示例 3客户端 服务端驱动组合HTTPlist op listPets(query pageIndex page?: int32 1, query pageSize perPage?: int32 100): { pageItems pets: Pet[]; // 链接中会解析出携带 page 与 perPage 的完整 URL links: { nextLink next?: url; prevLink prev?: url; firstLink first?: url; lastLink last?: url; }; };此处注释表明链接返回的 URL 是用当前page和perPage解析后的结果即服务器在生成链接时会把分页参数编码进 URL。附加参数的分页语义哪些会带到下一页分页操作可以携带与分页控制无关的附加参数如过滤参数filter。默认期望是这些参数会被延续到下一页请求唯一例外是链接next/prev/first/last场景——不同协议对链接究竟代表什么可能有不同解释。对于 HTTP 链接链接被期望为不透明的已包含下一页 URL 所需的全部信息query、path 参数应已编码进链接而header 参数无法被编码进链接因此客户端在跟随链接的后续请求中必须原样重发这些 header。场景 1HTTP 中的 next link 分页route(pets) list op listPets( query filter?: string, query expand?: string, query pageIndex page?: int32 1, query pageSize perPage?: int32 100, header specialHeader?: x-special-value, ): { pageItems pets: Pet[]; nextLink next?: url; };对应的两次请求交互如下// 第一次请求 GET /pets?filterdog Special-Header: x-special-value {pets: [...], nextLink: /pets?filterdogpage2perPage100} --- // 第二次请求 GET /pets?filterdogpage2perPage100 Special-Header: x-special-value {pets: [...], nextLink: /pets?filterdogpage3perPage100}可以看到query 参数filter、page、perPage均被编码进nextLink而specialHeader无法进入链接因此客户端在第二次请求中显式重发该 header。场景 2HTTP 中的续传令牌分页route(pets) list op listPets( query filter?: string, query expand?: string, query continuationToken token?: string, header specialHeader?: x-special-value, ): { pageItems pets: Pet[]; continuationToken next?: url; };// 第一次请求 GET /pets?filterdog Special-Header: x-special-value {pets: [...], continuationToken: token2} --- // 第二次请求 GET /pets?filterdogtokentoken2 Special-Header: x-special-value {pets: [...], continuationToken: token3}这里filter由客户端自行延续因为令牌模式没有服务器生成的链接token携带服务器下发的令牌specialHeader依旧被原样重发。编译器内部分页属性的收集与校验机制理解这些装饰器如何工作需要看编译器核心实现 paging.ts。所有分页装饰器共 9 个都由createMarkerDecorator工厂函数生成paging.ts本质是基于useStateSet的状态标记装饰器执行时把目标属性/操作打标后续通过isList、isOffsetProperty、isNextLink等断言函数查询。收集阶段由getPagingOperation完成它把分页属性分为两组paging.tsinputoffset、pageIndex、pageSize、continuationToken来自操作参数outputpageItems、nextLink、prevLink、firstLink、lastLink、continuationToken来自返回类型。关键行为嵌套属性收集navigateProperties会递归遍历模型、联合类型及基类baseModel的所有属性因此分页属性可以嵌套在返回模型的任意层级中路径由PagingProperty.pathModelProperty[]记录发射器可用path.map(p p.name).join(.)还原属性路径paging.ts。测试见 paging.test.ts。重复属性报错同一操作内相同类型的分页标记如两个nextLink会触发duplicate-paging-prop诊断paging.test.ts。冲突标记报错同一属性被多个不同类型的分页装饰器同时标注如nextLink prevLink next: string会触发incompatible-paging-props诊断paging.test.ts。递归模型安全navigateProperties通过visited集合防止自引用模型如selfRef?: MyPage导致死循环paging.test.ts。缺失pageItems报错getPagingOperation在输出中没有pageItems时返回undefined并报告missing-paging-items。这些能力通过公开 APIgetPagingOperation(program, op)暴露给下游发射器返回PagingOperation结构化数据输入/输出两侧的分页属性及其路径是发射器生成 OpenAPI、客户端代码或文档时的主要数据来源。与生态的衔接OpenAPI3 转换与既有规范分页装饰器并非孤立存在。在 openapi3 的转换工具 中可以看到当把已有 OpenAPI 规范转换回 TypeSpec 时x-ms-list-page-items扩展会被映射为pageItemsx-ms-list-continuation-token扩展会被映射为continuationTokenx-ms-list-next-link扩展映射为nextLink。这意味着分页元数据可以在 OpenAPI 与 TypeSpec 之间往返转换保证语义不丢失。小结TypeSpec 的分页模型用一套统一装饰器覆盖了两大主流分页范式客户端驱动offset/pageIndexpageSize由客户端推算请求参数服务端驱动nextLink/prevLink/firstLink/lastLink不透明链接HTTP 下默认 GET 跟随与continuationToken请求参数与响应成对出现由服务器下发导航信息混合模式两者可自由组合附加参数默认延续到下一页链接中已编码 query/path 参数header 参数需客户端重发。所有装饰器在编译器端经过严格的类型校验数值类型、数组类型、重复与冲突检测、缺失pageItems检测与结构化收集最终通过getPagingOperation供发射器消费。想要在自定义发射器中读取分页信息直接调用getPagingOperation(program, op)即可获得完整的输入/输出分页属性清单。【免费下载链接】typespec项目地址: https://gitcode.com/GitHub_Trending/ty/typespec创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →