登录接口报错返回HTML而非JSON的问题排查请求
问题:登录接口错误时返回HTML而非JSON响应
我的登录接口在遇到邮箱/密码缺失、学生不存在或邮箱未验证等错误时,服务器返回包含错误信息及堆栈跟踪的HTML响应,而前端期望JSON格式,无法正常处理这类响应。
错误响应示例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Error</title> </head> <body> <pre>Error: Student does not exist<br> at file:///C:/parag/CollegeProject/e-Learning-Platform/backend/src/controllers/student.controller.js:133:15<br> at process.processTicksAndRejections (node:internal/process/task_queues:95:5)</pre> </body> </html>
相关代码片段:
asyncHandler工具函数
const asyncHandler = (requestHandler) => { return (req,res,next) => { Promise.resolve(requestHandler(req,res,next)). catch((err)=>next(err)) } } export {asyncHandler}
自定义ApiError类
class ApiError extends Error{ constructor( statusCode, message = "Something went wrong", errors = [], stack = "" ){ super(message) this.statusCode = statusCode this.data = null, this.message = message this.success = false this.errors = errors if(stack){ this.stack = stack } else{ Error.captureStackTrace(this,this.constructor) } } } export {ApiError}
登录接口函数
const login = asyncHandler(async(req,res) => { const {Email, Password} = req.body; /*if(!Email){ throw new ApiError(400,"E-mail is required") } if(!Password){ throw new ApiError(400,"Password is required") }*/ const result = await authSchema.validateAsync(req.body) const StdLogin = await student.findOne({ Email }) if(!StdLogin){ throw new ApiError(400, "Student does not exist") } if(!StdLogin.Isverified){ throw new ApiError(401, "Email is not verified"); } const StdPassCheck = await StdLogin.isPasswordCorrect(Password) if(!StdPassCheck){ return res.status(400).json({ error: "Password is incorrect" }); } const tempStd = StdLogin._id const {Accesstoken, Refreshtoken} = await generateAccessAndRefreshTokens(tempStd) const loggedInStd = await student.findById(tempStd).select(-Password -Refreshtoken) const options = { httpOnly:true, secure:true, } return res .status(200) .cookie("Accesstoken", Accesstoken, options) .cookie("Refreshtoken", Refreshtoken, options) .json( new ApiResponse( 200,{ user:loggedInStd }, "logged in" ) ) })
解决方案
1. 注册全局错误处理中间件
在Express应用入口文件(如app.js)中,添加全局错误处理中间件,必须放在所有路由之后注册:
app.use((err, req, res, next) => { // 处理Joi验证错误,适配ApiError结构 if (err.isJoi) { err.statusCode = 400; err.message = err.details.map(detail => detail.message).join(', '); err.errors = err.details; } const statusCode = err.statusCode || 500; const message = err.message || 'Internal Server Error'; const errors = err.errors || []; // 仅开发环境返回堆栈信息,生产环境隐藏敏感内容 const stack = process.env.NODE_ENV === 'development' ? err.stack : undefined; return res.status(statusCode).json({ success: false, message, errors, ...(stack && { stack }) }); });
2. 统一错误抛出逻辑
修改登录接口中密码错误的处理代码,与其他错误保持一致,使用ApiError抛出:
// 原代码 if(!StdPassCheck){ return res.status(400).json({ error: "Password is incorrect" }); } // 修改后 if(!StdPassCheck){ throw new ApiError(400, "Password is incorrect"); }
原理说明
- Express默认错误处理中间件会返回HTML页面,自定义全局中间件可统一将所有错误转换为JSON格式响应。
- 针对Joi验证错误做特殊格式化,保证响应结构与自定义
ApiError一致。 - 开发环境返回堆栈信息便于调试,生产环境隐藏堆栈避免泄露服务器敏感路径。
- 统一使用
throw ApiError抛出错误,让所有错误都经过全局中间件处理,避免分散的响应逻辑。
内容的提问来源于stack exchange,提问作者Parag
相关产品推荐
相关产品推荐

