Node.js + Express REST API错误响应结构最佳实践咨询
Node.js Express REST API 错误响应最佳实践
一、是否所有错误都应遵循标准化格式?
是的,所有错误必须统一格式。前端团队无需针对不同接口的错误写不同解析逻辑,能大幅降低前端代码复杂度;同时统一格式也方便后端做日志收集、错误监控,后续扩展新接口时无需重新定义错误结构,保持一致性。
二、是否应包含内部错误代码?
必须包含。内部错误代码(如INVALID_EMAIL、DATABASE_CONN_FAILED)是机器可读的稳定标识,相比可变的文本提示,前端可以基于错误代码做精准逻辑处理(比如对PASSWORD_TOO_SHORT显示密码强度提示,对TOKEN_EXPIRED跳转登录页);后端排查问题时,通过错误代码能快速定位错误类型,无需解析模糊的文本消息。
三、向客户端暴露多少细节为宜?
- 生产环境:只暴露用户需要的必要信息,绝对禁止输出敏感细节(如数据库连接信息、服务器文件路径、完整堆栈跟踪)。比如数据库查询失败,只返回“服务暂时不可用,请稍后重试”,而不是泄露数据库连接串这类信息。
- 开发环境:可以返回完整错误详情(如堆栈跟踪、错误原因),方便前后端联合调试。
四、生产环境API通用认可模式
生产环境的错误响应通常包含以下核心字段,可根据业务需求扩展:
success: 布尔值,明确标记请求是否成功(false表示错误)message: 用户友好的提示文本,直接展示给终端用户code: 机器可读的错误码,用于前端逻辑判断和后端排查status: HTTP状态码,与响应头的状态码保持一致(可选,但能让前端快速获取状态)requestId: 请求唯一标识,用于关联后端日志,方便问题追踪timestamp: 错误发生的时间戳,便于排查时序问题details: 可选字段,针对验证类错误,返回具体字段的错误信息
实际场景示例
1. 验证错误(400 Bad Request)
{ "success": false, "message": "提交的表单信息有误", "code": "VALIDATION_FAILED", "status": 400, "requestId": "req-123456789", "timestamp": "2024-05-20T14:30:00Z", "details": [ { "field": "email", "error": "邮箱格式无效" }, { "field": "password", "error": "密码长度不能少于6位" } ] }
2. 未授权错误(401 Unauthorized)
{ "success": false, "message": "登录已过期,请重新登录", "code": "TOKEN_EXPIRED", "status": 401, "requestId": "req-987654321", "timestamp": "2024-05-20T14:31:00Z" }
3. 服务器内部错误(500 Internal Server Error)
{ "success": false, "message": "服务器暂时出现问题,请稍后重试", "code": "INTERNAL_SERVER_ERROR", "status": 500, "requestId": "req-456789123", "timestamp": "2024-05-20T14:32:00Z" }
Express 错误处理中间件实现示例
// 自定义错误类 class AppError extends Error { constructor(message, code, statusCode) { super(message); this.code = code; this.statusCode = statusCode; this.isOperational = true; // 标记为业务逻辑错误,区别于Node原生错误 Error.captureStackTrace(this, this.constructor); } } // 全局错误处理中间件 app.use((err, req, res, next) => { const env = process.env.NODE_ENV || 'development'; const requestId = req.headers['x-request-id'] || req.id; // 可通过中间件生成全局requestId let response = { success: false, message: err.message || '服务器错误', code: err.code || 'UNKNOWN_ERROR', status: err.statusCode || 500, requestId: requestId, timestamp: new Date().toISOString() }; // 开发环境返回堆栈信息辅助调试 if (env === 'development') { response.stack = err.stack; } // 适配Joi等验证库的错误格式 if (err.name === 'ValidationError') { response.code = 'VALIDATION_FAILED'; response.details = Object.values(err.errors).map(item => ({ field: item.path, error: item.message })); } // 设置响应头状态码并返回错误信息 res.status(response.status).json(response); }); // 路由中抛出自定义错误示例 app.post('/api/users', (req, res, next) => { const { email } = req.body; if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { return next(new AppError('邮箱格式无效', 'INVALID_EMAIL', 400)); } // 业务逻辑处理... });
内容的提问来源于stack exchange,提问作者Rahul Sharma
相关产品推荐
相关产品推荐

