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

在catch块中抛出新Error是否正确?分层架构错误处理咨询

你的错误处理思路分析与优化建议

你的这种错误处理思路方向是对的——在服务层封装业务相关的错误信息,控制器层统一处理返回给用户,能避免暴露底层细节,给用户清晰的提示。但具体实现里有几个明显的问题:

存在的问题

  • 错误场景误判:你把服务层捕获到的所有错误都统一抛出“User ID not found”,但实际getOneRecord可能抛出多种错误——比如网络超时、对方服务器500错误、用户存在但没有tax字段等,这些场景都被归为“用户不存在”,会误导用户和排查问题的开发人员。
  • 错误上下文丢失:抛出新Error时完全覆盖了原始错误的堆栈和细节,后续排查问题时根本不知道到底是axios请求失败,还是业务逻辑出错,排查难度大大增加。
  • 底层错误未分层处理:services.js里的getOneRecord直接暴露axios的原始错误,没有做任何包装,导致上层服务层无法区分不同类型的错误。

优化方案

1. 自定义错误类,区分错误类型

创建不同的自定义错误类,继承自原生Error,用来标记不同的错误场景:

class NotFoundError extends Error {
  constructor(message) {
    super(message);
    this.name = 'NotFoundError';
    this.statusCode = 404;
  }
}

class NetworkError extends Error {
  constructor(message) {
    super(message);
    this.name = 'NetworkError';
    this.statusCode = 503;
  }
}

class InvalidDataError extends Error {
  constructor(message) {
    super(message);
    this.name = 'InvalidDataError';
    this.statusCode = 400;
  }
}

2. 保留原始错误上下文

抛出错误时,用cause参数保留原始错误,方便后续排查:

// UserServices.js
async getTaxValue(id) {
  try {
    const user = await this.getOneRecord(id);
    if (user.tax === undefined || user.tax === null) {
      throw new InvalidDataError(`User ${id} has no tax information`);
    }
    return Number(user.tax) / 100;
  } catch (error) {
    // 根据原始错误类型重新包装
    if (error instanceof NotFoundError) {
      throw new NotFoundError(`User ID not found: ${id}`, { cause: error });
    } else if (error instanceof NetworkError) {
      throw new NetworkError(`Failed to fetch user data: ${error.message}`, { cause: error });
    }
    throw error;
  }
}

3. 底层服务先处理axios错误

在getOneRecord里先对axios的错误做分层处理,抛出对应的自定义错误:

// services.js
async getOneRecord(id) {
  const url = this.url + `/${id}`;
  try {
    const res = await axios.get(url);
    return res.data;
  } catch (error) {
    if (axios.isAxiosError(error)) {
      if (error.response?.status === 404) {
        throw new NotFoundError(`Resource not found at ${url}`);
      } else if (error.code === 'ECONNABORTED') {
        throw new NetworkError(`Request timed out for ${url}`);
      } else {
        throw new NetworkError(`Request failed: ${error.message}`);
      }
    }
    // 非axios错误直接抛出
    throw error;
  }
}

4. 全局错误处理代替重复try-catch

创建全局中间件统一处理错误,不用每个控制器都写try-catch:

// 全局错误处理中间件
function errorHandler(err, req, res, next) {
  const statusCode = err.statusCode || 500;
  const message = err.message || 'Internal Server Error';
  
  // 这里可以加入日志记录,把err.cause的信息也记录下来,方便排查
  console.error('Error details:', err);
  
  res.status(statusCode).json({ message });
}

// 在你的Express app中注册中间件
app.use(errorHandler);

// 此时控制器可以简化为:
// UserController.js
static async getUserTax(req, res) {
  const { userId } = req.params;
  const user = await userServices.getTaxValue(userId);
  return res.status(200).json(user);
}

调整后,既能给用户返回清晰准确的错误信息,又能保留足够的错误上下文方便排查,同时让代码的错误处理逻辑更健壮、可维护。

内容的提问来源于stack exchange,提问作者Lucas da Paz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 15:30:22