content-disposition、content-type、http-errors、mime、mime-types 和 type-is 是一组专注于处理 HTTP 请求/响应中常见任务的轻量级 Node.js 工具库。它们分别用于生成或解析 Content-Disposition 头、解析或格式化 Content-Type 头、创建标准化 HTTP 错误对象、查询文件扩展名与 MIME 类型的映射、提供权威的 MIME 类型数据库,以及根据请求内容判断媒体类型。这些库通常被 Express 等 Web 框架内部使用,也可在中间件或自定义服务逻辑中直接调用,以确保符合 HTTP 规范并提升代码健壮性。
在构建健壮的 Node.js Web 应用(尤其是使用 Express 或 Koa 时),正确处理 HTTP 头部、MIME 类型和错误响应至关重要。content-disposition、content-type、http-errors、mime、mime-types 和 type-is 这六个库虽小,却各自解决了一个关键问题。它们共同构成了现代 Web 框架底层的“HTTP 工具箱”。本文将深入剖析它们的功能差异、适用场景及协同方式。
每个库都聚焦于一个明确的 HTTP 相关任务:
content-disposition:生成安全的 Content-Disposition 响应头,用于控制浏览器如何处理响应内容(内联显示或作为附件下载)。content-type:解析或格式化 Content-Type 请求/响应头,提取媒体类型和参数(如 charset)。http-errors:创建标准化的 HTTP 错误对象,包含状态码、消息和可暴露性标记。mime:提供完整的 MIME 类型数据库及双向查询(扩展名 ↔ MIME 类型),支持运行时修改。mime-types:提供只读的 MIME 类型数据库查询接口,轻量高效。type-is:根据请求的 Content-Type 判断其是否匹配给定的媒体类型模式(支持通配符)。💡 注意:这些库均未被废弃,维护活跃,且被 Express 官方中间件广泛使用。
content-disposition vs 手动拼接当提供文件下载时,Content-Disposition 头必须正确编码文件名以支持中文等特殊字符。手动拼接极易出错。
// ❌ 危险:手动拼接可能被注入或乱码
const filename = '报告.pdf';
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
// ✅ 安全:使用 content-disposition 自动处理编码
const contentDisposition = require('content-disposition');
res.setHeader('Content-Disposition', contentDisposition('报告.pdf'));
// 输出: attachment; filename="%E6%8A%A5%E5%91%8A.pdf"; filename*=UTF-8''%E6%8A%A5%E5%91%8A.pdf
content-type 的精准解析Content-Type 头常包含参数(如 charset=utf-8),需结构化解析。
// 使用 content-type 解析
const contentType = require('content-type');
const parsed = contentType.parse('application/json; charset=utf-8');
// { type: 'application/json', parameters: { charset: 'utf-8' } }
// 反向生成标准格式
const formatted = contentType.format({ type: 'text/html', parameters: { charset: 'iso-8859-1' } });
// 'text/html; charset=iso-8859-1'
http-errors 的语义化错误对象抛出带状态码的错误,便于全局错误处理中间件识别。
const createError = require('http-errors');
// 创建 404 错误
throw createError(404, 'User not found');
// 创建 500 错误(默认不暴露细节)
throw createError.InternalServerError('Database connection failed');
// 错误对象属性
// err.status === 500
// err.expose === false (5xx 默认不暴露,4xx 默认暴露)
mime vs mime-types两者都基于 mime-db 数据库,但接口和能力不同。
// mime-types:只读查询
const mimeTypes = require('mime-types');
mimeTypes.contentType('.json'); // 'application/json'
mimeTypes.charset('text/html'); // 'UTF-8'
// mime:双向查询 + 运行时修改
const mime = require('mime');
mime.getType('file.json'); // 'application/json'
mime.getExtension('text/html'); // 'html'
// 添加自定义类型(仅 mime 支持)
mime.define({ 'application/x-custom': ['xc'] });
type-is 的灵活匹配在中间件中根据请求体类型执行不同逻辑。
const typeis = require('type-is');
// 检查是否为 JSON
if (typeis(req, ['json'])) {
// 处理 application/json
}
// 检查是否为文本类型(支持通配符)
if (typeis.is(req.headers['content-type'], ['text/*'])) {
// 处理 text/plain, text/html 等
}
// 检查是否为特定 vendor 类型
if (typeis(req, 'application/vnd.api+json')) {
// 处理 JSON:API
}
这些库常在 Web 框架中间件中协同工作。例如,一个文件上传处理流程:
type-is 检查请求是否为 multipart/form-data。content-type 解析边界参数(boundary)。mime-types 根据文件扩展名设置响应 Content-Type。content-disposition 生成安全的下载头。http-errors 抛出 404 错误。// Express 中间件示例
app.use('/download/:filename', (req, res, next) => {
const { filename } = req.params;
const filePath = path.join(__dirname, 'uploads', filename);
if (!fs.existsSync(filePath)) {
return next(createError(404)); // http-errors
}
const mimeType = mimeTypes.contentType(path.extname(filename)); // mime-types
res.setHeader('Content-Type', mimeType);
res.setHeader('Content-Disposition', contentDisposition(filename)); // content-disposition
res.sendFile(filePath);
});
| 场景 | 推荐库 | 原因 |
|---|---|---|
| 生成文件下载头 | content-disposition | 自动处理编码,符合 RFC 标准 |
| 解析/生成 Content-Type 头 | content-type | 精确处理类型和参数 |
| 抛出带状态码的错误 | http-errors | 标准化错误对象,含 expose 语义 |
| 需要动态添加 MIME 类型 | mime | 支持运行时 define() |
| 静态资源 MIME 查询 | mime-types | 轻量只读,接口简洁 |
| 按内容类型路由请求 | type-is | 支持通配符和复杂匹配 |
type-is 内部使用 mime-types)。content-disposition 和 http-errors 的 expose 属性能有效防止信息泄露。这六个库虽小,却是构建符合 HTTP 规范的 Web 服务不可或缺的基石。它们分工明确:
content-disposition、content-typehttp-errorsmime(可写)、mime-types(只读)type-is在架构设计中,优先使用这些经过实战检验的工具,而非自行实现 HTTP 相关逻辑,能显著提升应用的健壮性、安全性和可维护性。
选择 mime-types 当你只需要快速、只读地查询标准 MIME 类型数据库(如根据扩展名获取类型,或检测某类型是否为文本类型),而无需修改数据库。它接口简洁、体积小,是大多数 Web 应用中处理静态资源 MIME 类型的首选。
选择 content-type 当你需要解析请求中的 Content-Type 头(如 application/json; charset=utf-8)为结构化对象,或从类型和参数反向生成标准格式的 Content-Type 字符串。它严格遵循 RFC 7231,适合需要精确处理媒体类型和字符集参数的中间件或 API 网关。
选择 content-disposition 当你需要安全地生成符合 RFC 6266 标准的 Content-Disposition 响应头,特别是为用户下载文件时指定文件名(支持自动处理特殊字符和非 ASCII 字符的编码)。它能避免手动拼接字符串带来的安全风险和兼容性问题,是处理文件下载场景的可靠选择。
选择 http-errors 当你需要抛出带有标准 HTTP 状态码(如 404、500)和可读消息的错误对象。它生成的错误实例包含 status、statusCode 和 expose 属性,便于上层错误处理中间件统一格式化响应,是构建健壮 RESTful API 的基础组件。
选择 mime 当你需要一个包含完整 MIME 类型数据库(基于 mime-db)且支持双向查询(扩展名 ↔ MIME 类型)的工具,并希望直接操作本地 MIME 类型定义(如添加自定义类型)。它比 mime-types 提供了更多运行时操作能力,适合需要动态扩展 MIME 映射的场景。
选择 type-is 当你需要根据请求的 Content-Type 头判断其是否匹配特定的媒体类型(如 json、text/* 或 application/vnd.api+json)。它支持通配符和子类型匹配,常用于中间件中按内容类型路由请求或验证请求体格式。
The ultimate javascript content-type utility.
Similar to the [email protected] module, except:
mime-types simply returns false, so do
var type = mime.lookup('unrecognized') || 'application/octet-stream'.new Mime() business, so you could do var lookup = require('mime-types').lookup..define() functionality.lookup(path)Otherwise, the API is compatible with mime 1.x.
This is a Node.js module available through the
npm registry. Installation is done using the
npm install command:
$ npm install mime-types
This package considers the programmatic api as the semver compatibility. Additionally, the package which provides the MIME data
for this package (mime-db) also considers it's programmatic api as the semver contract. This means the MIME type resolution is not considered
in the semver bumps.
In the past the version of mime-db was pinned to give two decision points when adopting MIME data changes. This is no longer true. We still update the
mime-db package here as a minor release when necessary, but will use a ^ range going forward. This means that if you want to pin your mime-db data
you will need to do it in your application. While this expectation was not set in docs until now, it is how the pacakge operated, so we do not feel this is
a breaking change.
If you wish to pin your mime-db version you can do that with overrides via your package manager of choice. See their documentation for how to correctly configure that.
All mime types are based on mime-db, so open a PR there if you'd like to add mime types.
var mime = require('mime-types')
All functions return false if input is invalid or not found.
Lookup the content-type associated with a file.
mime.lookup('json') // 'application/json'
mime.lookup('.md') // 'text/markdown'
mime.lookup('file.html') // 'text/html'
mime.lookup('folder/file.js') // 'application/javascript'
mime.lookup('folder/.htaccess') // false
mime.lookup('cats') // false
Create a full content-type header given a content-type or extension.
When given an extension, mime.lookup is used to get the matching
content-type, otherwise the given content-type is used. Then if the
content-type does not already have a charset parameter, mime.charset
is used to get the default charset and add to the returned content-type.
mime.contentType('markdown') // 'text/x-markdown; charset=utf-8'
mime.contentType('file.json') // 'application/json; charset=utf-8'
mime.contentType('text/html') // 'text/html; charset=utf-8'
mime.contentType('text/html; charset=iso-8859-1') // 'text/html; charset=iso-8859-1'
// from a full path
mime.contentType(path.extname('/path/to/file.json')) // 'application/json; charset=utf-8'
Get the default extension for a content-type.
mime.extension('application/octet-stream') // 'bin'
Lookup the implied default charset of a content-type.
mime.charset('text/markdown') // 'UTF-8'
A map of content-types by extension.
A map of extensions by content-type.