Express接口错误本该返回JSON却返回HTML导致前端JSON解析报错
异常根因说明
你观察到的「响应头Content-Type为text/html、返回内容是带<html><body><pre>标签的默认错误页」,是Express框架内置兜底错误页的典型特征。出现这个问题的核心原因是:你写的用来返回JSON的全局错误处理中间件,根本没有被执行到,请求抛出业务错误后直接落到了Express默认的HTML错误返回逻辑上。
结合你提到的「此前服务运行正常、近期突然出现异常」的背景,按出现概率从高到低,常见的触发原因有三个:
- 中间件挂载顺序错误
Express的中间件严格按照代码书写的从上到下顺序匹配执行,带4个参数(error, req, res, next)的全局错误处理中间件,必须放在所有业务路由、普通中间件的最后面挂载。如果近期调整代码时,把错误处理中间件挪到了路由注册逻辑的前面,抛出的错误会直接绕过这个自定义逻辑,落到框架默认兜底。 - 被前端SPA适配逻辑拦截
前后端分离项目通常会加一段匹配所有路径的兜底逻辑,用来解决前端history路由刷新404的问题(比如用app.get('*', (req, res) => res.sendFile('index.html'))让所有非接口请求返回前端页面)。这类逻辑都是不带错误参数的普通中间件,如果这段代码放在了全局错误处理中间件的前面,哪怕是接口请求抛出错误,也会先被这个返回HTML的逻辑接住,根本走不到后面的JSON返回逻辑——这也是很多项目原本正常、加完SPA路由适配后突然出现接口返回HTML问题的最常见踩坑点。 - 自定义错误中间件未被成功注册
比如修改错误中间件代码时,漏写了app.use(...)的注册调用,或者这段注册逻辑被if条件判断包裹,实际运行时根本没有执行到注册流程。
排查&解决方案
- 先验证中间件的执行状态
你已经在自定义错误中间件里加了console.log('error middleware: ' + error)的日志,现在触发一次密码错误的登录请求,观察服务端控制台的输出:
- 如果完全没有打出这行日志:100%是中间件挂载顺序错误。按照「先注册所有业务路由→再注册静态资源、SPA页面兜底逻辑→最后注册全局错误处理中间件」的顺序调整代码,参考结构:
// 第一步:注册所有业务接口路由 app.use('/auth', authRouter) app.use('/api', apiRouter) // 第二步:注册静态资源、前端SPA路由兜底 app.use(express.static('public')) app.get('*', (req, res) => res.sendFile(path.join(__dirname, 'public/index.html'))) // 第三步:必须在代码最后面注册全局错误处理中间件 app.use((error, req, res, next) => { const message = error.message; const status = error.statusCode || 500; const data = error.data; // 手动设置响应头,强制走JSON返回,避免被框架默认逻辑覆盖 res.setHeader('Content-Type', 'application/json') res.status(status).json({ message, data }); })
- 如果日志正常打出,但仍然返回HTML内容
检查错误中间件内部的执行流程:确认在调用res.status(status).json(...)之前,没有其他代码提前触发过res.send()、res.render()、res.redirect()等会主动结束响应的逻辑,导致JSON返回代码被中断。 - 针对前端抛出的
SyntaxError: Unexpected token < in JSON at position 0报错
这个报错不需要调整前端代码,本质是后端返回的HTML字符串第一个字符是<,前端调用res.json()解析时自然会抛出语法错误。等后端的返回逻辑修正为JSON格式后,这个报错会自动消失。
调整完成后可以通过浏览器网络面板检查登录请求的响应头,确认
Content-Type为application/json、响应内容是结构化JSON数据,即为修复完成。
内容的提问来源于stack exchange,提问作者msorr95
相关产品推荐
相关产品推荐

