Express与MongoDB:如何在路由控制器中手动抛出错误?
问题分析与解决方案
核心问题拆解
- 错误URL无法进入控制器:只有请求URL匹配
/api/v1/tours时才会触发getAllTours,不匹配的URL会直接走到app.all('*'),这是Express的路由匹配逻辑,无法改变。控制器的错误处理只针对合法路由下的业务异常(比如查询结果为空)。 - 自定义错误类存在语法/继承问题:TypeScript与CommonJS混合写法导致类型混乱,继承逻辑不完整。
- 异步错误捕获需确保依赖正确引入:
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
相关产品推荐
相关产品推荐

