news 2026/9/19 0:10:12

TypeSpec Rest 资源操作模板接口全解:@typespec/rest 中 22 个 Resource Interface 的完整参考指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TypeSpec Rest 资源操作模板接口全解:@typespec/rest 中 22 个 Resource Interface 的完整参考指南

TypeSpec Rest 资源操作模板接口全解:@typespec/rest 中 22 个 Resource Interface 的完整参考指南

【免费下载链接】typespec项目地址: https://gitcode.com/GitHub_Trending/ty/typespec

@typespec/restTypeSpec.Rest.Resource命名空间提供了一组资源操作模板接口(Resource Operation Template Interfaces),把 REST 资源最常见的 CRUD 生命周期操作抽象为可复用的泛型接口。本指南以官方参考文档 interfaces.md 为骨架,逐一拆解全部 22 个接口的模板参数、操作签名与适用场景,并结合 resource.tsp 的源码实现与 resource.test.ts 的测试用例,说明这些模板如何被解析为真实的 HTTP 路由。读完本文,你将能熟练选用合适的模板接口(标准资源、单例资源、扩展资源),理解其背后的键参数复制与路由生成机制,并直接在自己的 TypeSpec 规范中落地使用。

一、资源操作模板解决了什么问题

在 REST API 设计中,"资源"的读写删建等操作高度模式化:几乎每个资源都需要get(读取单个实例)、update(更新)、delete(删除)、create(创建)、list(分页列出)这五个标准操作。如果每个资源都手工书写这些op,规范会迅速变得冗长且难以保持一致。

@typespec/rest通过接口模板(interface 配合模板参数)解决这一问题:你只需要定义一个资源模型(如Thing),再写一句interface Things extends ResourceOperations<Thing, Error> {},编译器就会自动生成完整的标准操作集合,包括正确的路由、HTTP 动词、路径参数与响应类型。

这些模板接口定义在TypeSpec.Rest.Resource命名空间中,按适用对象可分为三类:

类别接口
标准资源模板ResourceReadResourceUpdateResourceDeleteResourceCreateResourceListResourceCreateOrReplaceResourceCreateOrUpdateResourceInstanceOperationsResourceCollectionOperationsResourceOperations
单例资源模板SingletonResourceReadSingletonResourceUpdateSingletonResourceOperations
扩展资源模板ExtensionResourceReadExtensionResourceUpdateExtensionResourceDeleteExtensionResourceCreateExtensionResourceListExtensionResourceCreateOrUpdateExtensionResourceInstanceOperationsExtensionResourceCollectionOperationsExtensionResourceOperations

接口的权威定义位于 packages/rest/lib/resource.tsp,TypeSpec 编译器会在编译时对模板进行实例化并生成路由。

二、理解模板之前必须掌握的基础构件

所有资源操作模板都建立在一组底层模型与装饰器之上,理解它们才能真正读懂接口签名。

2.1 装饰器:@key@segment@resource@parentResource

资源模板要求资源模型上至少有一个键属性(key property),即用@key标记的属性,它会被复制为操作中的路径参数。@resource("collectionName")将模型标记为资源类型,并自动在其@key属性上应用@segment@segment则为该路径参数定义前置路径段。@parentResource(parent)用于声明父子资源关系,使子资源的操作自动带上父资源的键参数。

关于这些装饰器的完整签名与参数说明,可参考 README.md 或 decorators.md。

2.2 键参数收集模型:KeysOfParentKeysOf

模板签名中大量出现的...ResourceParameters<Resource>...ResourceCollectionParameters<Resource>是路由参数展开的入口,它们最终依赖两个"动态收集"模型:

// packages/rest/lib/resource.tsp 中的定义 @copyResourceKeyParameters @friendlyName("{name}Key", Resource) model KeysOf<Resource> {} @copyResourceKeyParameters("parent") @friendlyName("{name}ParentKey", Resource) model ParentKeysOf<Resource> {}
  • KeysOf<Resource>:动态收集Resource模型(含其父链)上的全部@key属性;
  • ParentKeysOf<Resource>:只收集父资源的键属性(当@copyResourceKeyParameters的过滤参数为"parent"时),用于集合级操作定位父级作用域。

在此基础上:

model ResourceParameters<Resource extends {}> { ...KeysOf<Resource>; } model ResourceCollectionParameters<Resource extends {}> { ...ParentKeysOf<Resource>; }

ResourceParameters用于实例级操作(如getupdatedelete),展开后包含资源自身及其所有父级的键;ResourceCollectionParameters用于集合级操作(如createlist),只包含父级键。这一机制的底层实现在 src/resource.ts 的cloneKeyProperties$copyResourceKeyParameters函数中——源码会递归遍历父资源链,将每个键属性克隆到目标模型,强制optional: false(防止可选的键属性变成可选路径参数),并自动附加@path装饰器。

2.3 请求与响应模型

模板签名中还引用了若干辅助模型,它们同样定义在TypeSpec.Rest.Resource命名空间:

  • ResourceCreateModel<Resource>:创建操作请求体,等价于DefaultKeyVisibility<Resource, Lifecycle.Read>并施加Lifecycle.Create可见性,即创建时通常不可提供服务端生成的键;
  • ResourceCreateOrUpdateModel<Resource>:创建/更新操作请求体,是Resource的可更新属性集合(OptionalProperties<UpdateableProperties<...>>),属性均可选以支持部分更新;
  • ResourceCreatedResponse<Resource>:创建成功响应,HTTP 状态码固定为201@bodyRoot body承载被创建的资源;
  • ResourceDeletedResponse:删除成功响应,HTTP 状态码固定为200
  • CollectionWithNextLink<Resource>:分页响应结构,包含value: Resource[](当前页数据,@pageItems)与可选的nextLink?: ResourceLocation<Resource>(下一页链接,@nextLink);
  • ResourceError:默认错误响应模型,含code: int32message: string

上述模型的属性明细可在>interface TypeSpec.Rest.Resource.ResourceRead<Resource, Error> op get(...ResourceParameters<Resource>): Resource | Error;

签名中的...ResourceParameters<Resource>展开后即为@path @segment("things") thingId: string之类的参数。在 resource.test.ts 中,interface Things extends ResourceRead<Thing, Error> {}被解析为GET /things/{thingId}

ResourceUpdate<Resource, Error>—— 更新单个资源:

op update(...ResourceParameters<Resource>, properties: ResourceCreateOrUpdateModel<Resource>): Resource | Error;

更新操作使用@patch并开启implicitOptionality(为兼容旧行为),因此生成的路由动词为patch。源码在 resource.tsp 中还标注了@updatesResource(Resource)装饰器。

ResourceDelete<Resource, Error>—— 删除单个资源:

op delete(...ResourceParameters<Resource>): ResourceDeletedResponse | Error;

删除成功后返回ResourceDeletedResponse(200 状态码),源码实现见 resource.tsp。

ResourceInstanceOperations<Resource, Error>—— 实例级三件套的聚合模板:

interface ResourceInstanceOperations<Resource extends {}, Error> extends ResourceRead<Resource, Error>, ResourceUpdate<Resource, Error>, ResourceDelete<Resource, Error> {}

它通过 interface 继承组合了getupdatedelete三个操作,定义见 resource.tsp。

3.2 集合级操作模板

集合级操作作用于资源集合,路由中只包含父级键参数(通常没有键参数)。

ResourceCreate<Resource, Error>—— 创建资源:

op create(...ResourceCollectionParameters<Resource>, resource: ResourceCreateModel<Resource>): Resource | ResourceCreatedResponse<Resource> | Error;

创建操作使用...ResourceCollectionParameters(只带父键),请求体为ResourceCreateModel,成功时返回ResourceResourceCreatedResponse(201 + 创建后的资源体)。测试验证其路由为POST /things

ResourceList<Resource, Error>—— 列出资源集合:

op list(...ResourceCollectionParameters<Resource>): CollectionWithNextLink<Resource> | Error;

返回分页结构CollectionWithNextLink<Resource>,路由为GET /things

ResourceCollectionOperations<Resource, Error>—— 集合级两件套的聚合模板:

interface ResourceCollectionOperations<Resource extends {}, Error> extends ResourceCreate<Resource, Error>, ResourceList<Resource, Error> {}

组合了createlist,见 resource.tsp。

3.3 完整生命周期模板:ResourceOperations

interface ResourceOperations<Resource extends {}, Error> extends ResourceInstanceOperations<Resource, Error>, ResourceCollectionOperations<Resource, Error> {}

ResourceOperations聚合了全部五个标准操作:getupdatedeletecreatelist。这是日常使用频率最高的模板——一行interface Things extends ResourceOperations<Thing, Error> {}即可获得完整的 REST 资源端点。测试 resource.test.ts 验证其生成的路由为:

GET /things/{thingId} PATCH /things/{thingId} DELETE /things/{thingId} POST /things GET /things

3.4 两种特殊写操作模板

ResourceCreateOrReplace<Resource, Error>—— 全量创建或替换:

op createOrReplace(...ResourceParameters<Resource>, resource: ResourceCreateModel<Resource>): Resource | ResourceCreatedResponse<Resource> | Error;

create不同,createOrReplace使用ResourceParameters(包含资源自身的键)并生成PUT动词,语义为"按指定键全量替换",路由为PUT /things/{thingId},见测试 resource.test.ts。

ResourceCreateOrUpdate<Resource, Error>—— 创建或部分更新(upsert 语义):

op createOrUpdate(...ResourceParameters<Resource>, resource: ResourceCreateOrUpdateModel<Resource>): Resource | ResourceCreatedResponse<Resource> | Error;

同样携带资源自身键参数,请求体为ResourceCreateOrUpdateModel(属性可选),使用@patch生成PATCH动词。源码见 resource.tsp。

四、单例资源模板:SingletonResource*

单例资源(singleton resource)表示在父资源作用域下唯一存在的一个实例,例如GET /things/{thingId}/settings中的settings。它的特殊之处在于:路径中只有父资源的键,而没有单例自身的键。

单例模板使用三个类型参数:Singleton(单例资源模型)、Resource(父资源模型)、Error

SingletonResourceRead<Singleton, Resource, Error>

op get(...ResourceParameters<Resource>): Singleton | Error;

操作只展开父资源的键参数,并通过@segmentOf(Singleton)使用单例模型上的@segment生成其 URL 段。测试 resource.test.ts 中,@segment("singleton") model SingletonSingletonResourceOperations<Singleton, Thing, Error>配合,生成了GET /things/{thingId}/singleton

SingletonResourceUpdate<Singleton, Resource, Error>

op update(...ResourceParameters<Resource>, properties: ResourceCreateOrUpdateModel<Singleton>): Singleton | Error;

请求体为基于Singleton模型的可选属性集合,生成PATCH /things/{thingId}/singleton

SingletonResourceOperations<Singleton, Resource, Error>—— 聚合以上两者:

interface SingletonResourceOperations<Singleton extends {}, Resource extends {}, Error> extends SingletonResourceRead<Singleton, Resource, Error>, SingletonResourceUpdate<Singleton, Resource, Error> {}

单例资源只有读取与更新两个标准操作,因为创建/删除/列表在语义上对单例不适用。

五、扩展资源模板:ExtensionResource*

扩展资源(extension resource)是一种附着在其他资源(父资源或子资源)之上的资源类型,用于在不修改原资源模型的前提下为它"扩展"额外数据与操作。扩展资源模板同样使用三个类型参数:Extension(扩展资源模型)、Resource(被扩展的资源模型)、Error

关键差异在于签名中同时展开两组键参数,例如ExtensionResourceRead

op get(...ResourceParameters<Resource>, ...ResourceParameters<Extension>): Extension | Error;

这表示路由同时包含被扩展资源的键与扩展资源自身的键。以测试 resource.test.ts 中的ExtensionResourceOperations<Exthing, Thing, Error>为例,生成的路由为:

GET /things/{thingId}/extension/{exthingId} PATCH /things/{thingId}/extension/{exthingId} DELETE /things/{thingId}/extension/{exthingId} POST /things/{thingId}/extension GET /things/{thingId}/extension

而当扩展挂在子资源(Subthing)上时,路由变为/things/{thingId}/subthings/{subthingId}/extension/{exthingId},可见父键会被完整继承。

全部 9 个扩展资源模板及其操作如下(均定义于 resource.tsp):

接口操作签名要点
ExtensionResourceReadget...ResourceParameters<Resource>, ...ResourceParameters<Extension>
ExtensionResourceUpdateupdate两组键参数 +properties: ResourceCreateOrUpdateModel<Extension>
ExtensionResourceDeletedelete两组键参数,返回ResourceDeletedResponse
ExtensionResourceCreatecreate...ResourceParameters<Resource>+resource: ResourceCreateModel<Extension>
ExtensionResourceListlist...ResourceParameters<Resource>+...ResourceCollectionParameters<Extension>,返回CollectionWithNextLink<Extension>
ExtensionResourceCreateOrUpdatecreateOrUpdate两组键参数 +ResourceCreateOrUpdateModel<Extension>
ExtensionResourceInstanceOperationsget/update/delete聚合 Read、Update、Delete
ExtensionResourceCollectionOperationscreate/list聚合 Create、List
ExtensionResourceOperationsget/update/delete/create/list聚合以上两个

六、模板参数的约定与编译期校验

所有模板的类型参数遵循统一约定:

参数含义要求
Resource标准资源模型必须含@key属性(否则诊断resource-missing-key
Extension扩展资源模型必须含@key属性
Singleton单例资源模型无需键,但需@segment定位
Error错误响应模型必须用@error装饰(否则诊断resource-missing-error

在 resource.tsp 源码中,多数模板接口上都标注了@Private.validateHasKey(Resource)@Private.validateIsError(Error),对应 resource.test.ts 中验证的两条诊断:

  • @typespec/rest/resource-missing-key:"Type 'Dog' is used as a resource and therefore must have a key. Use @key to designate a property as the key."
  • @typespec/rest/resource-missing-error:"Type 'Error' is used as an error and therefore must have the @error decorator applied."

此外还有针对资源建模本身的校验:资源模型上存在多个@key时报告duplicate-key;子资源键名与父资源键名冲突时报告duplicate-parent-key@parentResource形成循环时报告circular-parent-resource(源码实现见 src/resource.ts 中的checkCircularParentResource,测试见 resource.test.ts)。

七、实战:如何选用并组合这些模板

7.1 标准资源:一行接口获得完整 CRUD

import "@typespec/rest"; import "@typespec/http"; using TypeSpec.Rest; using TypeSpec.Rest.Resource; @resource("things") model Thing { @key @segment("things") thingId: string; name: string; description?: string; } @error model Error { code: int32; message: string; } interface Things extends ResourceOperations<Thing, Error> {}

编译后即得到GET/PATCH/DELETE /things/{thingId}POST/GET /things五个端点。

7.2 分层资源:借助@parentResource组合嵌套路由

@parentResource(Thing) @resource("subthings") model Subthing { @key @segment("subthings") subthingId: string; data: string; } interface Subthings extends ResourceOperations<Subthing, Error> {}

依据 resource.test.ts,这会生成/things/{thingId}/subthings/{subthingId}(get/patch/delete)与/things/{thingId}/subthings(post/get),父级键thingId自动出现在所有子操作的路由中。

7.3 单例资源

@segment("settings") model ThingSettings { theme: string; } interface ThingSettingsSingleton extends SingletonResourceOperations<ThingSettings, Thing, Error> {}

生成GET /things/{thingId}/settingsPATCH /things/{thingId}/settings

7.4 扩展资源

@resource("tags") model Tag { @key @segment("tags") tagId: string; label: string; } interface ThingTags extends ExtensionResourceOperations<Tag, Thing, Error> {}

生成GET/PATCH/DELETE /things/{thingId}/tags/{tagId}POST/GET /things/{thingId}/tags,为Thing资源附加标签扩展能力。

7.5 组合使用

接口模板可以同时继承多个模板,例如测试中的interface Things extends ResourceOperations<Thing, Error>, ResourceCreateOrReplace<Thing, Error> {},它会在五个标准操作之外再增加一个PUT /things/{thingId}的替换操作,见 resource.test.ts。

八、进一步阅读

  • 接口参考原文(本文档)
  • 数据模型参考
  • 装饰器参考
  • @typespec/rest 总览
  • 资源路由与自动路由生成
  • 源码实现 与 装饰器运行时实现
  • 路由生成与诊断的测试用例
  • 包级 README 与装饰器清单

【免费下载链接】typespec项目地址: https://gitcode.com/GitHub_Trending/ty/typespec

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 0:09:09

Visual Studio C/C++调试完全指南:断点、内存与崩溃定位技巧

1. 调试&#xff0c;才是写代码的真正分水岭很多初学者学C/C时&#xff0c;最容易陷入一个误区&#xff1a;花大量时间背语法、刷例题&#xff0c;却在程序跑出错误结果后手足无措&#xff0c;只能一句一句地读代码肉眼找bug。遇到稍复杂一点的场景——指针乱飞、数组越界、内存…

作者头像 李华
网站建设 2026/9/19 0:08:55

Windows 11装VMware 12报错:Runtime DLL失败与升级16

上周同事把他的笔记本抱过来&#xff0c;屏幕上就停在一个弹窗上&#xff1a;「安装程序无法继续。Microsoft Runtime DLL安装程序未能完成安装。」他装的是 VMware 12&#xff0c;系统是刚换的 Windows 11。他的判断很直接——运行库坏了&#xff0c;修运行库就行。我看了两分…

作者头像 李华
网站建设 2026/9/19 0:08:51

PX4自定义消息映射:uORB与MAVLink通信全链路解析

1. 为什么PX4里自定义消息不能“写完就用”&#xff1f;——uORB与MAVLink的双层通信真相你是不是也遇到过这样的情况&#xff1a;在PX4源码里新增了一个uORB消息&#xff0c;比如叫vehicle_wind_estimate&#xff0c;编译烧录后飞控能正常发布&#xff0c;但QGC地面站死活收不…

作者头像 李华
网站建设 2026/9/19 0:08:20

MCP 集成复杂度还是 M×N?TaoToken 这样改 Cursor 的模型设置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 0:07:36

一文搞懂Linux静态库与动态库:从原理到实战

1. 库文件到底是个什么东西1.1 从一段代码变成可执行程序&#xff0c;中间经历了什么很多人在Linux下敲过gcc main.c -o main&#xff0c;一条命令就能得到可执行文件&#xff0c;于是想当然地认为编译就是把源代码变成二进制。其实这一步背后藏着一整套流程&#xff1a;预处理…

作者头像 李华
网站建设 2026/9/19 0:07:00

杰理之关机时序异常修复ASSERT-FAILD【篇】

关机后出现 assert 异常 在调用 rcsp_interface_bt_handle_tws_send_in_task() 函数时&#xff0c;如果系统已经进入关机流程&#xff08;app_var.goto_poweroff_flag 置位&#xff09;&#xff0c;该函数仍会继续执行 TWS 数据同步操作。由于关机过程中部分资源已被释放或状态…

作者头像 李华