You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 23:45:08