Express错误处理器返回HTML而非JSON问题求助
问题原因分析
- 当前错误处理器仅针对
req.xhr为true的请求生效,但现代前端请求(如fetch/axios)通常不会触发req.xhr,导致错误被传递到Express默认错误处理器,返回HTML页面。 - 错误中间件的位置可能不正确,未放在所有路由之后,无法捕获路由中抛出的错误。
- 错误处理器未利用自定义错误类的
statusCode和message,也未区分自定义错误与普通错误。 - 需确认
asyncHandler是否正确捕获异步错误并传递给next。
解决方案
1. 修正错误处理器逻辑,支持JSON请求
替换原clientErrorHandler,改为根据请求类型返回JSON,或统一返回JSON格式错误:
import { Request, Response, NextFunction } from 'express'; import { StatusCodes } from 'http-status-codes'; function errorHandler(err: any, req: Request, res: Response, next: NextFunction) { // 优先使用自定义错误的状态码和消息 const statusCode = err.statusCode || StatusCodes.INTERNAL_SERVER_ERROR; const message = err.message || 'Something went wrong'; // 针对API请求返回JSON,浏览器请求可按需返回HTML if (req.accepts('json') || req.headers['content-type']?.includes('application/json')) { res.status(statusCode).json({ success: false, error: message, // 开发环境可保留堆栈信息,生产环境建议移除 ...(process.env.NODE_ENV === 'development' && { stack: err.stack }) }); } else { // 浏览器请求返回简化HTML(可选,也可统一返回JSON) res.status(statusCode).send(`<h1>${statusCode} Error</h1><p>${message}</p>`); } } // 必须放在所有路由和其他中间件之后 app.use(errorHandler);
2. 确保错误中间件的位置
Express中间件按顺序执行,错误中间件必须放在所有路由、路由挂载(如app.use('/api', router))之后,示例:
// 先挂载所有业务路由 app.use('/api/auth', authRouter); app.use('/api/users', userRouter); // 最后挂载错误处理中间件 app.use(errorHandler);
3. 完善自定义错误类
修正UnauthenticatedError的构造函数,确保TypeScript原型链正确:
import { StatusCodes } from 'http-status-codes'; import CustomAPIError from './custom-errors'; class UnauthenticatedError extends CustomAPIError { public statusCode: number; constructor(message: string) { super(message); this.statusCode = StatusCodes.UNAUTHORIZED; // 修复TypeScript继承自定义错误的原型链问题 Object.setPrototypeOf(this, UnauthenticatedError.prototype); } } export default UnauthenticatedError;
父类CustomAPIError建议实现:
export default class CustomAPIError extends Error { constructor(message: string) { super(message); Object.setPrototypeOf(this, CustomAPIError.prototype); } }
4. 验证asyncHandler的正确性
确保asyncHandler能捕获异步函数的错误并传递给next,正确实现:
import { Request, Response, NextFunction } from 'express'; const asyncHandler = (fn: (req: Request, res: Response, next: NextFunction) => Promise<any>) => { return (req: Request, res: Response, next: NextFunction) => { Promise.resolve(fn(req, res, next)).catch(next); }; }; export default asyncHandler;
5. 测试验证
触发认证错误后,客户端应收到类似如下的JSON响应:
{ "success": false, "error": "There is no token attached to the Header.", "stack": "Error: There is no token attached to the Header.\n at authMiddleware.ts:44:15..." }
内容的提问来源于stack exchange,提问作者user824624
相关产品推荐
相关产品推荐

