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

Express与MongoDB:如何在路由控制器中手动抛出错误?

问题分析与解决方案

核心问题拆解

  1. 错误URL无法进入控制器:只有请求URL匹配/api/v1/tours时才会触发getAllTours,不匹配的URL会直接走到app.all('*'),这是Express的路由匹配逻辑,无法改变。控制器的错误处理只针对合法路由下的业务异常(比如查询结果为空)。
  2. 自定义错误类存在语法/继承问题:TypeScript与CommonJS混合写法导致类型混乱,继承逻辑不完整。
  3. 异步错误捕获需确保依赖正确引入:express-async-errors必须在Express和路由之前导入才能生效。

1. 修复自定义错误类(统一TypeScript写法)

// httpStatusCode.ts
export const httpStatusCode = {
  NOT_FOUND: 404,
  INTERNAL_SERVER_ERROR: 500
};

// CustomError.ts
import { httpStatusCode } from './httpStatusCode';

export class CustomError extends Error {
  statusCode: number;
  status: string;

  constructor(message: string, statusCode: number) {
    super(message);
    this.statusCode = statusCode;
    this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
    Object.setPrototypeOf(this, CustomError.prototype);
  }
}

// NotFoundError.ts
import { CustomError } from './CustomError';
import { httpStatusCode } from './httpStatusCode';

export class NotFoundError extends CustomError {
  constructor(message: string = '资源未找到') {
    super(message, httpStatusCode.NOT_FOUND);
    Object.setPrototypeOf(this, NotFoundError.prototype);
  }
}

2. 修正错误处理中间件

// errorHandler.ts
import { Request, Response, NextFunction } from 'express';
import { CustomError } from '../utils/CustomError';

export const errorHandler = (
  err: Error,
  req: Request,
  res: Response,
  next: NextFunction
) => {
  if (err instanceof CustomError) {
    return res.status(err.statusCode).json({
      status: err.status,
      message: err.message
    });
  }

  // 处理未知系统错误
  console.error('未知错误:', err);
  res.status(500).json({
    status: 'error',
    message: '服务器内部错误'
  });
};

3. 调整控制器逻辑(处理合法路由下的空结果)

// tourController.ts
import { Request, Response } from 'express';
import { Tour } from '../models/Tour';
import { APIFeatures } from '../utils/APIFeatures';
import { NotFoundError } from '../utils/NotFoundError';

export const getAllTours = async (req: Request, res: Response) => {
  const features = new APIFeatures(Tour.find(), req.query)
    .filter()
    .sort()
    .limitFields()
    .paginate();

  const tours = await features.query;

  // 当查询结果为空时抛出业务错误
  if (!tours || tours.length === 0) {
    throw new NotFoundError('未找到任何旅游产品');
  }

  res.status(200).json({
    status: 'success',
    result: tours.length,
    data: { tours }
  });
};

4. 确保入口文件的依赖与路由顺序正确

// server.ts
import 'express-async-errors'; // 必须在Express之前导入
import express from 'express';
import { errorHandler } from './middleware/errorHandler';
import tourRouter from './routes/tourRoutes';

const app = express();

// 基础中间件
app.use(express.json());

// 业务路由
app.use('/api/v1/tours', tourRouter);

// 未匹配路由处理(放在所有路由之后)
app.all('*', (req: Request) => {
  throw new NotFoundError(`无法找到路径: ${req.originalUrl}`);
});

// 错误处理中间件(必须放在最后)
app.use(errorHandler);

// 启动服务
const PORT = process.env.PORT || 8000;
app.listen(PORT, () => {
  console.log(`服务器运行在端口 ${PORT}`);
});

关键注意事项

  • app.all('*')的作用是处理所有未匹配的路由,这是正常逻辑,无法让错误URL进入特定控制器。
  • 控制器内的错误处理仅针对路由匹配但业务逻辑异常的场景(比如过滤后无结果、参数非法等)。
  • express-async-errors会自动捕获异步控制器中的错误并传递给错误处理中间件,无需手动调用next(err)。

内容的提问来源于stack exchange,提问作者Orion

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 00:10:26