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

Express错误处理器返回HTML而非JSON问题求助

问题原因分析
  1. 当前错误处理器仅针对req.xhr为true的请求生效,但现代前端请求(如fetch/axios)通常不会触发req.xhr,导致错误被传递到Express默认错误处理器,返回HTML页面。
  2. 错误中间件的位置可能不正确,未放在所有路由之后,无法捕获路由中抛出的错误。
  3. 错误处理器未利用自定义错误类的statusCode和message,也未区分自定义错误与普通错误。
  4. 需确认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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 12:22:40