关于 error-responses
用于 API 错误处理的编码技能,指导开发者避免向客户端暴露内部信息,并返回一致的结构化错误。
主要功能
- 01结构化错误响应
- 02机器可读错误码
- 03HTTP 状态码指引
- 04通过请求 ID 查询日志
适用场景
- 01实现 API 错误处理
- 02检查控制器异常响应
- 03替换返回给客户端的原始错误
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ yanko-belov/ code-craft --skill error-responses安装后在 Claude Code 中输入/error-responses确认已加载。
详细介绍
错误响应
概述
绝不暴露内部错误。返回结构化、安全的错误响应。
原始错误消息会泄露实现细节、助长攻击者的行动,并让用户困惑。错误响应应安全、一致且可操作。
适用场景
- 在 API 中实现错误处理
- 向客户端返回错误响应
- 在控制器中捕获异常
- 有人要求“直接返回错误消息”
铁律
NEVER expose raw error messages or stack traces to clients.
没有例外:
- 不能因为“有助于调试”
- 不能因为“仅供内部 API 使用”
- 不能因为“我们处于开发环境”
- 不能因为“前端需要详细信息”
检测:泄露迹象
如果错误暴露了内部信息,立即停止:
// ❌ VIOLATION: Exposing internals
app.get('/users/:id', async (req, res) => {
try {
const user = await db.query('SELECT * FROM users WHERE id = $1', [req.params.id]);
res.json(user);
} catch (error) {
res.status(500).json({ message: error.message }); // Leaks!
}
});
可能泄露的信息:
"relation \"users\" does not exist"- 数据库架构"connect ECONNREFUSED 10.0.1.5:5432"- 内部 IP 地址- 包含文件路径的堆栈跟踪
- 包含表名的 SQL 查询
正确模式:安全的错误响应
// ✅ CORRECT: Structured, safe errors
// Custom error classes
class AppError extends Error {
constructor(
public statusCode: number,
public code: string,
message: string
) {
super(message);
}
}
class NotFoundError extends AppError {
constructor(resource: string) {
super(404, 'NOT_FOUND', `${resource} not found`);
}
}
class ValidationError extends AppError {
constructor(public details: Record<string, string[]>) {
super(400, 'VALIDATION_ERROR', 'Validation failed');
}
}
// Error handler middleware
app.use((error: Error, req: Request, res: Response, next: NextFunction) => {
// Log full error internally
console.error('Error:', {
message: error.message,
stack: error.stack,
path: req.path,
method: req.method,
});
// Return safe response
if (error instanceof ValidationError) {
return res.status(400).json({
error: {
code: error.code,
message: error.message,
details: error.details,
}
});
}
if (error instanceof AppError) {
return res.status(error.statusCode).json({
error: {
code: error.code,
message: error.message,
}
});
}
// Unknown errors - never expose
res.status(500).json({
error: {
code: 'INTERNAL_ERROR',
message: 'An unexpected error occurred',
}
});
});
// Usage in routes
app.get('/users/:id', async (req, res, next) => {
try {
const user = await userService.findById(req.params.id);
if (!user) throw new NotFoundError('User');
res.json(user);
} catch (error) {
next(error); // Pass to error handler
}
});
错误响应结构
所有错误使用一致的结构:
interface ErrorResponse {
error: {
code: string; // Machine-readable: 'VALIDATION_ERROR'
message: string; // Human-readable: 'Validation failed'
details?: unknown; // Additional info (validation errors, etc.)
requestId?: string; // For support/debugging
}
}
// Examples:
// 400 Bad Request
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": {
"email": ["Invalid email format"],
"age": ["Must be at least 18"]
}
}
}
// 404 Not Found
{
"error": {
"code": "NOT_FOUND",
"message": "User not found"
}
}
// 500 Internal Error
{
"error": {
"code": "INTERNAL_ERROR",
"message": "An unexpected error occurred",
"requestId": "req_abc123"
}
}
HTTP 状态码
| 代码 | 使用场景 |
|---|---|
| 400 | 请求错误、验证错误 |
| 401 | 未通过身份验证 |
| 403 | 已通过身份验证,但未获授权 |
| 404 | 未找到资源 |
| 409 | 冲突(重复项、状态问题) |
| 422 | 无法处理的实体 |
| 429 | 请求过于频繁 |
| 500 | 服务器错误(隐藏详情!) |
| 502 | 上游服务故障 |
| 503 | 服务不可用 |
抵御施压的准则
1. “有助于调试”
施压说法:“开发人员需要查看完整错误”
**回应:**在服务器端记录完整错误。返回请求 ID 以便关联查询。
操作:{ requestId: "abc123" } - 开发人员可据此查找日志。
2. “仅供内部 API 使用”
施压说法:“只有我们的服务会调用它”
**回应:**内部服务也可能遭到入侵,日志也可能泄露。所有内容都要保护。
**操作:**在所有地方采用相同的安全错误处理方式。
3. “开发模式”
施压说法:“开发环境显示详情,生产环境隐藏”
**回应:**开发环境的代码会进入生产环境。保持良好的习惯很重要。
**操作:**所有环境都采用相同的处理方式。使用日志进行调试。
危险信号——立即停止并重新考虑
res.json({ message: error.message })- 在响应中包含堆栈跟踪
- 在错误消息中包含 SQL 查询
- 暴露内部 IP 地址或路径
- 各端点使用不同的错误格式
出现以上任何情况,都意味着:实施恰当的错误处理。
快速参考
| 暴露信息(错误做法) | 安全做法 |
|---|---|
| 数据库错误消息 | “发生错误” |
| 堆栈跟踪 | 提供用于查找日志的请求 ID |
| 内部路径 | 通用错误代码 |
| SQL 查询 | “验证失败” |
| 变量转储 | 结构化错误对象 |
常见托词(均不成立)
| 借口 | 实际情况 |
|---|---|
| “有助于调试” | 在服务器端记录,并返回请求 ID。 |
| “内部 API” | 内部系统也可能遭到入侵。 |
| “开发模式” | 所有环境都采用相同的处理方式。 |
| “前端需要详细信息” | 返回安全、结构化的详细信息。 |
| “这样更快” | 错误处理成本很低,数据泄露却代价高昂。 |
归根结底
在内部记录所有信息。绝不向外部暴露任何信息。
返回包含机器可读代码和人类可读消息的一致结构化错误。绝不泄露堆栈跟踪、查询或内部详情。使用请求 ID 进行调试。
评论
支持 Markdown