NodeJS服务类与控制器的错误处理实现方案咨询
如何在Node.js中实现带错误状态码的Service层
你当前的核心问题是:Service层抛出的都是普通Error,控制器无法区分错误类型,导致所有错误统一返回500状态码。要解决这个问题,关键是让Service层的错误携带足够的标识信息,让控制器能映射到对应的HTTP状态码,同时保持Service不依赖req/res对象。
下面提供两种实用方案,按需选择:
方案一:自定义错误类(推荐)
通过创建继承自原生Error的自定义错误类,给不同业务错误绑定对应的HTTP状态码。Service抛出特定错误后,控制器根据错误的statusCode返回响应。
步骤1:定义自定义错误类
新建errors.js文件(也可以直接写在Service文件中):
// 基础错误类,所有自定义错误继承它 class AppError extends Error { constructor(message, statusCode) { super(message); this.statusCode = statusCode; this.name = this.constructor.name; // 标记错误类型,方便调试 Error.captureStackTrace(this, this.constructor); // 保留错误栈信息 } } // 对应400参数无效错误 class ValidationError extends AppError { constructor(message) { super(message, 400); } } // 对应404资源不存在错误 class NotFoundError extends AppError { constructor(message) { super(message, 404); } } // 对应403权限不足错误 class ForbiddenError extends AppError { constructor(message) { super(message, 403); } } export { AppError, ValidationError, NotFoundError, ForbiddenError };
步骤2:修改Service层,抛出自定义错误
注意:你的原Service代码中getPerson方法用了await但没加async,这会触发语法错误,必须补上:
import Person from "../models/person.js"; import mongoose from "mongoose"; import { ValidationError, NotFoundError, ForbiddenError } from "./errors.js"; class PersonService { // 必须添加async关键字 async getPerson(id) { if(!mongoose.Types.ObjectId.isValid(id)){ throw new ValidationError('Invalid ID'); } const person = await Person.findById(id); if (!person) { throw new NotFoundError("Not found"); } if (person.Age < 18) { throw new ForbiddenError('Cannot query for minors'); } return person; } } export default PersonService;
步骤3:控制器处理错误
捕获错误后,根据错误的statusCode返回对应响应,未知错误默认用500:
import PersonService from "../services/PersonService.js"; async function getPerson(req, res) { const personService = new PersonService(); try{ const personData = await personService.getPerson(req.params.id); res.status(200).send(personData) } catch (error) { console.error(error); // 提取状态码,默认500 const statusCode = error.statusCode || 500; // 提取错误消息,默认兜底内容 const message = error.message || "Error retrieving media"; res.status(statusCode).send(message); } } export { getPerson };
方案二:返回结果对象(适合新手快速上手)
如果觉得自定义错误类复杂,可以让Service层不抛出错误,而是返回一个包含成功/失败状态的对象,里面携带错误信息和对应状态码。
修改Service层
import Person from "../models/person.js"; import mongoose from "mongoose"; class PersonService { async getPerson(id) { try { if(!mongoose.Types.ObjectId.isValid(id)){ return { success: false, error: { message: 'Invalid ID', statusCode: 400 } }; } const person = await Person.findById(id); if (!person) { return { success: false, error: { message: "Not found", statusCode: 404 } }; } if (person.Age < 18) { return { success: false, error: { message: 'Cannot query for minors', statusCode: 403 } }; } return { success: true, data: person }; } catch (dbError) { console.error(dbError); return { success: false, error: { message: "Error retrieving media", statusCode: 500 } }; } } } export default PersonService;
修改控制器
直接根据返回的结果对象处理响应:
import PersonService from "../services/PersonService.js"; async function getPerson(req, res) { const personService = new PersonService(); const result = await personService.getPerson(req.params.id); if (result.success) { res.status(200).send(result.data); } else { res.status(result.error.statusCode).send(result.error.message); } } export { getPerson };
两种方案对比
- 自定义错误类:符合JavaScript/Node.js错误处理规范,能自动捕获意外错误(比如数据库连接失败),扩展性强,适合中大型项目。
- 返回结果对象:逻辑直观,新手易理解,但需要手动捕获Service内的所有异常,适合小型项目或快速原型开发。
内容的提问来源于stack exchange,提问作者heradsinn
相关产品推荐
相关产品推荐

