完整指南:默认推断、显式声明与协商)
TypeSpec HTTP 内容类型Content-Type完整指南默认推断、显式声明与协商【免费下载链接】typespec项目地址: https://gitcode.com/GitHub_Trending/ty/typespec导读本文围绕typespec/http库中的内容类型处理机制展开系统讲解 TypeSpec 在未显式声明Content-Type时的默认推断规则、通过header contentType显式指定请求/响应媒体类型的写法、基于Accept头的多格式协商方案共享路由与重载以及 multipart 请求的扩展方向。读完本文你将掌握在 TypeSpec 中为 HTTP 服务精确建模二进制、文本、JSON 与多格式响应体并确保其与底层 HTTP 语义一致的完整实战方案。本文以仓库文档 content-types.md 为主体骨架结合typespec/http库源码payload.ts、content-types.ts与标准库装饰器定义decorators.tsp进行原理级佐证与扩充。默认行为Content-Type 的推断规则当操作中没有显式指定Content-Type 时typespec/http会依据请求/响应体的类型自动推断。文档给出的默认映射如下application/json当 body 是 Model模型或包含null的 Union联合类型时application/octet-stream当 body 是TypeSpec.bytes或继承自它的 scalar 类型时除非该 scalar 自带mediaTypeHinttext/plain当 body 是其他任何没有mediaTypeHint的 scalar 类型时。文档中的完整示例// Returns an application/octet-stream binary body op download(): bytes; // Returns a text/plain string op getContent(): string; // Returns an application/json body that is either a string or the null value op getContentNullable(): string | null; // Returns an application/json body with a name property. op getPet(): { name: string; };源码层面的推断逻辑在 payload.ts 中resolveContentTypesForBody函数实现了上述推断几个关键分支值得注意字面量归一化若 body 是字符串、数字、布尔字面量或字符串模板会先被替换回对应的标准类型string、boolean、numeric再继续推断避免字面量干扰结果。encode解包对于带encode的 scalar 或属性会循着编码链一路解包到底层真实类型后再判断。含null的 Union 折叠为 JSONUnion 中只要存在null变体默认直接折叠为application/json源码注释明确说明这是默认情况下的处理策略。不含null的 Union 求并集对每个变体递归解析 Content-Type最终合并为一个去重后的集合——这正是文档中“多个 Content-Type 值”得以成立的底层基础。默认兜底非 Union 类型走getMediaTypeHint(...) ?? getDefaultContentType(type)其中默认值由 getDefaultContentTypeForKind 决定scalar →text/plain其余Model 等→application/json。mediaTypeHint可继承的媒体类型提示默认推断中的mediaTypeHint来自 TypeSpec 标准库。其定义位于 decorators.tsp要点如下可应用于Model | Scalar | Enum | Union参数为valueof string媒体类型字符串它只是提示emitter 和库可以选择使用也可能被覆盖或忽略typespec/http正是用它作为未显式声明时 body 的默认 Content-Type提示可被子类型继承模型上的mediaTypeHint会被所有extend它的模型继承除非子类型声明了自己的 hint媒体类型遵循 RFC 6838MIME 类型但装饰器不强制校验其合法性。例如定义一个默认序列化为 XML 的模型mediaTypeHint(application/xml) model Example { id: string; name: string; }从源码可见TypeSpec.bytes在标准库中被显式注入了application/octet-stream的 hintdecorators.tsp而TypeSpec.string的text/plainhint 为避免初始化循环被硬编码在编译器中注释中对此有明确说明。适用范围请求与响应一致上述推断逻辑对请求和响应体同样适用并且当使用了body或bodyRoot时会使用 body 的精确类型而非整个操作参数模型进行推断。这意味着body指定的单一属性直接参与推断bodyRoot指定的根对象按该对象的精确类型推断未使用二者时则以参数模型/返回模型中非元数据属性的整体结构为准。显式指定 Content-Type要精确控制媒体类型只需在操作中包含一个名为contentType的header参数即可header 名不区分大小写contentType会自动映射为content-type。header装饰器定义于 decorators.tsp。请求 Content-Typeop uploadImage(header contentType: image/png, body image: bytes): void;响应 Content-Typeop downloadImage(): { header contentType: image/png; body image: bytes; };多个 Content-Type 值同一个 body 支持多种媒体类型用字符串 Union 表达即可op uploadImage(header contentType: image/png | image/jpeg, body image: bytes): void;源码 content-types.ts 中getContentTypes函数展示了这一解析过程属性类型是String字面量 → 直接取其值属性类型是Union→ 遍历所有变体收集其中字符串字面量的值若出现非字符串变体则报content-type-string诊断属性类型是stringscalar → 返回[*/*]即通配所有媒体类型其他情况 → 返回空列表并产生content-type-string诊断。也就是说header contentType: string会被解析为*/*在语义上表示“接受任意内容类型”。Content-Type 协商同一端点返回多种格式当同一端点需要根据请求方希望的格式返回不同内容时TypeSpec 提供了两种建模方式两者都依赖Accept头与响应中的contentType联动。下面以“根据Accept头返回 png 或 jpeg 头像”为例。Option 1使用共享路由shared route共享路由的核心是sharedRoute装饰器定义于 decorators.tsp它标记操作与其他操作共享同一个路由路径前提是所有共享该路径的操作都带sharedRoute且只能直接作用于 Operation。model PngImage { header contentType: image/png; body image: bytes; } model JpegImage { header contentType: image/jpeg; body image: bytes; } route(/avatar) sharedRoute op getAvatarAsPng(header accept: image/png): PngImage; route(/avatar) sharedRoute op getAvatarAsJpeg(header accept: image/jpeg): JpegImage;两个操作共享/avatar路径分别通过Accept: image/png与Accept: image/jpeg区分响应的contentType则由返回模型中的header contentType字面量精确给出。Option 2使用重载overload重载方案以overload装饰器表达“同一路径、不同 Accept 输入、不同响应格式”的关系model PngImage { header contentType: image/png; body image: bytes; } model JpegImage { header contentType: image/jpeg; body image: bytes; } route(/avatar) op getAvatar(header accept: image/png | image/jpeg): PngImage | JpegImage; overload(getAvatar) op getAvatarAsPng(header accept: image/png): PngImage; overload(getAvatar) op getAvatarAsJpeg(header accept: image/jpeg): JpegImage;基操作getAvatar描述了端点的完整形态接受 png 或 jpeg返回对应格式两个重载分别细化到单一媒体类型。重载与共享路由的取舍共享路由更贴近“不同操作物理上共享端点”的路由表视角重载则强调“同一逻辑操作的不同变体”在工具链如客户端生成中通常能产生更统一的操作命名。Multipart 请求当 body 需要以multipart/form-data形式上传时typespec/http提供专门的 multipart 建模支持multipart/form-data、multipart/mixed等。核心细节请参阅 multipart 请求与响应文档。从 payload.ts 可看到multipart 类型在解析时通过contentTypes.some((x) x.startsWith(multipart/))进行识别并配合multipart-invalid-content-type诊断校验显式指定的 Content-Type 必须落在受支持的 multipart 类型集合内。因此在使用header contentType指定 multipart 媒体类型时务必使用受支持的multipart/*值。小结与最佳实践场景推荐写法效果二进制下载/上传op download(): bytes;默认application/octet-stream纯文本op getContent(): string;默认text/plainJSON 模型/可空联合op getPet(): { name: string };默认application/json精确指定单一格式header contentType: image/png字面量直接作为 Content-Type多格式输入header contentType: image/png \| image/jpegUnion 展开为多个媒体类型按 Accept 协商sharedRoute或overload同一端点按请求头返回不同格式要点回顾未显式声明时typespec/http按 body 类型推断默认媒体类型推断逻辑位于 payload.ts自定义 scalar 可通过mediaTypeHint可被子类型继承改变默认序列化格式显式指定用header contentType字面量或字符串 Union底层由 getContentTypes 解析string类型对应*/*多格式协商优先考虑sharedRoute或overload两种建模方式multipart 场景请进一步参考 multipart.md。【免费下载链接】typespec项目地址: https://gitcode.com/GitHub_Trending/ty/typespec创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考