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

登录接口报错返回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> &nbsp; &nbsp;at file:///C:/parag/CollegeProject/e-Learning-Platform/backend/src/controllers/student.controller.js:133:15<br> &nbsp; &nbsp;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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 00:48:12