MIME 类型
用于识别文件和数据类型的标准分类系统。以 type/subtype 格式表示。
MIME 类型 (Multipurpose Internet Mail Extensions) 是互联网上用于识别文件和数据类型的标准分类方式。以 type/subtype 格式表示,如 text/html、application/json、image/png。它最初是为了通过电子邮件发送非文本数据而制定的规范,如今则被用于以 HTTP 通信为首的互联网协议全域。另外,在 IANA 的登记簿和 RFC 中它被称为 "媒体类型",而在 HTML 规范和 Web 开发现场则被称为 "MIME 类型"。两者所指的是同一样东西,跨资料查阅时把两个词都查一遍可以减少遗漏。
在 HTTP 通信中,通过在 Content-Type 头中指定 MIME 类型,浏览器和客户端就能判断数据的处理方法。例如指定 Content-Type: text/html; charset=utf-8,浏览器就会把响应解释为 UTF-8 编码的 HTML。若指定了错误的 MIME 类型,就会发生 CSS 不生效 (返回 text/plain 而非 text/css)、JavaScript 不执行等问题。
主要的 MIME 类型类别有文本类 (text/plain、text/html、text/css)、应用类 (application/json、application/pdf、application/xml)、图像类 (image/png、image/jpeg、image/webp)、音频类 (audio/mpeg)、视频类 (video/mp4)。文本类的 MIME 类型可以用 charset 参数指定字符编码,写作 text/html; charset=utf-8 这样的形式。
不过 charset 并不是可以附加到任何 MIME 类型上的通用参数。在 application/json 的登记中,可选参数被记为 "n/a",charset 参数本身没有被定义 (RFC 8259),因此即使写成 application/json; charset=utf-8,遵循规范的接收方的行为也不会改变。这是因为 JSON 以用 UTF-8 发送为前提。JavaScript 的媒体类型也在整理中推进,RFC 9239 (2022 年) 把 text/javascript 定为唯一的推荐,而 application/javascript 和 text/ecmascript 等则被作为废止处理 (OBSOLETE)。无论返回哪一个,现行浏览器都会执行脚本,但如果是新写配置,选择 text/javascript 才顺理成章。
在 Web 开发的实务中,MIME 类型的设置失误有时会引发安全上的问题。为了防止滥用浏览器的 MIME 嗅探 (忽略 Content-Type 而从数据内容推测类型的行为) 的攻击,建议设置 X-Content-Type-Options: nosniff 头。另外,在文件上传功能中,重要的是不信任客户端发送的 MIME 类型,而在服务器一侧校验文件的实际内容。
一个常见的误解是把文件的扩展名与 MIME 类型等同起来。扩展名不过是文件系统上的惯例,并不会就那样乘上 HTTP 的往来。接收方用作判断材料的,是服务器在 Content-Type 头中声明的 MIME 类型。不过许多 Web 服务器在内部持有扩展名与 MIME 类型的对应表,所以改变扩展名,被声明的 MIME 类型也会随之改变。例如把 app.js 改名为 app.txt,即使内容仍是 JavaScript,也会作为 text/plain 被分发,在 X-Content-Type-Options: nosniff 生效的环境下,来自 script 元素的加载会被阻止。整理一下,扩展名、服务器的声明、文件的实际内容是三个相互独立、可能彼此矛盾的信息。在切分原因时,先用浏览器的开发者工具确认实际返回的 Content-Type,再从那里追查服务器的对应表,是最为可靠的做法。
从字符计数的角度看,MIME 类型在 API 设计和数据交换中承担着决定文本数据字符编码的重要作用。同样的日文文本在 charset=utf-8 和 charset=shift_jis 下字节数不同,因此会影响 Content-Length 头的值。要准确把握 API 响应的字符数,需要把 MIME 类型中指定的编码考虑进去。