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

Node.js错误处理中间件未生效,应用遇错崩溃无响应求解

问题:Express错误处理中间件未捕获异步控制器异常,导致服务器崩溃

我在后端设置了errorHandlerMiddleware错误处理中间件来处理异常,但它未按预期工作:每次出现错误时,应用直接崩溃,服务器无法返回正确响应。推测是authController.js中抛出的异常未被server.js中的errorHandlerMiddleware捕获,从而导致服务器崩溃。

Error Handler

const errorHandlerMiddleware = (err, req, res, next)=>{
    console.log(err)
    const defaultError = {
        statusCode: err.statusCode || 500,
        msg: err.message || "Something Went Wrong, Try Again Later",
    }
    if(err.name==="ValidationError"){
        defaultError.statusCode = 400
        defaultError.msg = Object.values(err.errors).map((item)=> item.message).join(', ')
    }
    if(err.code && err.code === 11000){
        defaultError.statusCode = 400
        defaultError.msg= `${Object.keys(err.keyValue)} has to be unique`
    }
    res.status(defaultError.statusCode).json({msg: defaultError.msg})
}

export default errorHandlerMiddleware

Server

import express from "express";
const app = express();
dotenv.config();
import notFound from "./middleware/NotFound.js";
import errorHandlerMiddleware from "./middleware/errorHandlerMiddleware.js";
import authRouter from "./routes/authRouter.js";
import dotenv from "dotenv";
import connectDB from "./db/connect.js";
app.use(express.json());
app.get("/", (req, res) => {
  res.send("Welcome");
});
app.get("/api/v1", (req, res) => {
  res.json({ msg: "API" });
});
app.use("/api/v1/auth", authRouter);
app.use(notFound);
app.use(errorHandlerMiddleware);
const port = process.env.PORT || 5000;

const start = async () => {
  try {
    await connectDB(process.env.MONGO_URL);
    app.listen(port, () => {
      console.log(`Server is running on port ${port}`);
    });
  } catch (error) {
    console.log(error);
  }
};

start();

Auth Controller

import {BadRequestError, UnauthenticatedError} from "../errorPackage/index.js"
import User from '../models/User.js'
const register = async(req, res) =>{
    const {name, email, password} = req.body
    if(!name || !email || !password){
        throw new BadRequestError("Please Provide All Values")
    }
    const alreadyUser = await User.findOne({email})
    if(alreadyUser){
        throw new BadRequestError("Email In Use")
    }
        const user = await User.create(req.body)
        const token = user.createJWT()
        res.json({
            user:{
                name: user.name,
                email: user.email,
            },
            token,
        })
        console.log('register')

}
const login = async(req, res) =>{
    const{email, password} = req.body
if(!email || !password){
    throw new BadRequestError("Please Provide All Values")
}
const user = await User.findOne({email}).select('+password')
if(!user){
    throw new UnauthenticatedError("Invalid Email")
}
const passwordMatch = await user.comparePassword(password)
if(!passwordMatch){
    throw new UnauthenticatedError("Invalid Credentials")
}
const token = await user.createJWT()
user.password = undefined;
res.status(200).json({user, token})
console.log('login success')
}
const updateUser = async(req, res) =>{
    res.send('Update')
}

export {register, login, updateUser}

authRouter

import express from "express";
const router = express.Router()
import { register, login, updateUser} from "../controllers/authController.js";

router.route('/register').post(register)
router.route('/login').post(login)
router.route('/updateUser').patch(updateUser)

export default router

错误示例

当发送注册请求时使用已存在的邮箱,会出现如下错误:

[0] Server is running on port 5000
[0] file:///C:/Users/Bharat/Desktop/MERNPrj/controllers/authController.js:10
[0]         throw new BadRequestError("Email In Use")
[0]               ^
[0]
[0] BadRequestError: Email In Use
[0]     at register (file:///C:/Users/Bharat/Desktop/MERNPrj/controllers/authController.js:10:15)
[0]     at processTicksAndRejections (node:internal/process/task_queues:96:5) {
[0]   statusCode: 400
[0] }
[0] [nodemon] app crashed - waiting for file changes before starting...

我猜测应用在服务器返回正确响应前就崩溃了。


解决方案

问题核心:Express 默认不会自动捕获async函数中抛出的异常,这些未被捕获的异常会直接终止 Node.js 进程,无法触发错误处理中间件。

提供两种修复方式:

方式一:用try/catch包裹异步逻辑,传递异常给next

修改authController.js中的异步函数,添加next参数并在catch块中传递错误:

const register = async(req, res, next) =>{ // 新增next参数
    try {
        const {name, email, password} = req.body
        if(!name || !email || !password){
            throw new BadRequestError("Please Provide All Values")
        }
        const alreadyUser = await User.findOne({email})
        if(alreadyUser){
            throw new BadRequestError("Email In Use")
        }
        const user = await User.create(req.body)
        const token = user.createJWT()
        res.json({
            user:{
                name: user.name,
                email: user.email,
            },
            token,
        })
        console.log('register')
    } catch (err) {
        next(err) // 将错误传递给错误处理中间件
    }
}

const login = async(req, res, next) =>{ // 新增next参数
    try {
        const{email, password} = req.body
        if(!email || !password){
            throw new BadRequestError("Please Provide All Values")
        }
        const user = await User.findOne({email}).select('+password')
        if(!user){
            throw new UnauthenticatedError("Invalid Email")
        }
        const passwordMatch = await user.comparePassword(password)
        if(!passwordMatch){
            throw new UnauthenticatedError("Invalid Credentials")
        }
        const token = await user.createJWT()
        user.password = undefined;
        res.status(200).json({user, token})
        console.log('login success')
    } catch (err) {
        next(err) // 将错误传递给错误处理中间件
    }
}

方式二:封装异步捕获工具函数(更简洁)

新建工具函数统一处理异步异常,避免重复写try/catch:

// utils/asyncWrapper.js
const asyncWrapper = (fn) => {
    return async (req, res, next) => {
        try {
            await fn(req, res, next);
        } catch (err) {
            next(err);
        }
    };
};

export default asyncWrapper;

在authRouter.js中使用工具函数包裹控制器:

import express from "express";
const router = express.Router()
import { register, login, updateUser} from "../controllers/authController.js";
import asyncWrapper from "../utils/asyncWrapper.js";

router.route('/register').post(asyncWrapper(register))
router.route('/login').post(asyncWrapper(login))
router.route('/updateUser').patch(asyncWrapper(updateUser))

export default router

原理说明

Express错误处理中间件仅捕获两类异常:

  1. 同步代码中直接抛出的异常
  2. 通过next(err)手动传递的异常

async函数中抛出的异常会转化为Promise拒绝状态,若未被捕获或传递,会成为未处理的Promise拒绝,直接导致进程崩溃。上述两种方式都是将异步异常转化为next(err)的形式,让错误处理中间件能够捕获并返回规范响应。


内容的提问来源于stack exchange,提问作者Het Patel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 14:06:19