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

Express中间件执行顺序异常:路由处理函数提前触发求助

Express中间件执行顺序异常:路由处理函数先于authenticatedUser中间件执行

我正在使用firebase-admin为Express后端服务器做身份认证,接口由React应用触发。预期中间件按getUserToken > authenticatedUser > postNewWarehouse的顺序执行,但实际输出显示路由处理函数postNewWarehouse在authenticatedUser中间件之前就触发了。

服务器代码

index.js

const express = require('express');
const cors = require("cors");
const jwt = require("jsonwebtoken");
const mongoose = require('mongoose');
require("dotenv").config();
const app = express();
const port = process.env.PORT || 5000;


app.use(cors());
app.use(express.json());

const inventoryRoutes = require('./routes/inventoryRoutes');

//global middleware
app.use((req, res, next) => {
    console.log(req.path, req.method);
    next();
})

//routes
app.use('/api',inventoryRoutes);

//welcome route
app.get("/", (req, res) => {
    res.send("Hello");
})



//Database connection
mongoose.connect(`mongodb+srv://${process.env.DB_USER}:${process.env.DB_PASS}@cluster0.wbmcg.mongodb.net/skus?retryWrites=true&w=majority`, { dbName: "inventoryDB" })
    .then(() => {
        console.log('connected to db');
    })
    .catch((error) => {
        console.log(error);
    })


//start Server
app.listen(port, () => {
    console.log("Inventory server is running on port ", port);
})

inventoryRoutes.js

const express = require('express');
const router = express.Router();
const { getUserToken, authenticatedUser } = require('../middleware/userAuthMiddleware')

//create a warehouse
router.post('/createwarehouse', getUserToken, authenticatedUser, postNewWarehouse)

module.exports = router

userAuthMiddleware.js

const admin = require('../firebase-config')


const getUserToken = async (req, res, next) => {
    const token = req.headers.authorization.split(' ')[1];
    const decodedValue = await admin.auth().verifyIdToken(token);
    console.log("at getUserToken middleware, logged in user email is ->  ", decodedValue.email);
    req.email = decodedValue.email;
    next()
}


const authenticatedUser = ( req, res, next ) => {
    admin.auth().listUsers()
        .then(response => {
            connectedUser = req.email;
            console.log("at authenticatedUser middleware, logged in user email is ->  ",connectedUser);

            const user = [];
            response.users.forEach((userRecord) => {
                user.push(userRecord.toJSON().email)
            })
            // console.log(user);
            const email = user.find(newuser => newuser === connectedUser)
            console.log(email, "  -> email is found in user list");
            
        })
    next()
}

module.exports = {
    authenticatedUser,
    getUserToken
}

实际控制台输出

/api/createwarehouse POST
at getUserToken middleware, logged in user email is ->   hello1@gello.com
from the main function of the route ->  { name: 'ertyyju', space: '23456', description: 'New warehouseerty' }
at authenticatedUser middleware, logged in user email is ->   hello1@gello.com
hello1@gello.com   -> email is found in user list

尝试过的解决方式

我试过把前两个中间件合并成数组,但问题依旧:

const middlewares = [getUserToken, authenticatedUser]
router.post('/createwarehouse', middlewares, postNewWarehouse)

请问为什么路由处理函数会在authenticatedUser中间件之前执行?


问题原因

问题出在authenticatedUser中间件的异步操作处理上:admin.auth().listUsers()是异步Promise操作,你在调用这个方法后立刻执行了next(),没有等待异步逻辑完成。这导致Express直接跳过中间件的剩余异步代码,先进入下一个处理环节(postNewWarehouse),等listUsers()的Promise完成后才会执行then回调里的日志代码,所以输出顺序混乱。

解决方法

把next()放到异步操作的回调末尾,确保只有在身份验证逻辑完成后,才让请求进入下一个处理步骤:

回调写法修改

const authenticatedUser = ( req, res, next ) => {
    admin.auth().listUsers()
        .then(response => {
            connectedUser = req.email;
            console.log("at authenticatedUser middleware, logged in user email is ->  ",connectedUser);

            const user = [];
            response.users.forEach((userRecord) => {
                user.push(userRecord.toJSON().email)
            })
            const email = user.find(newuser => newuser === connectedUser)
            console.log(email, "  -> email is found in user list");
            
            // 异步操作完成后再调用next()
            next()
        })
        // 新增错误处理,避免请求挂起
        .catch(error => {
            console.error('获取用户列表失败:', error);
            res.status(500).json({ error: '身份验证失败' });
        })
}

Async/Await写法(更清晰)

const authenticatedUser = async ( req, res, next ) => {
    try {
        const response = await admin.auth().listUsers();
        connectedUser = req.email;
        console.log("at authenticatedUser middleware, logged in user email is ->  ",connectedUser);

        const user = response.users.map(userRecord => userRecord.toJSON().email);
        const email = user.find(newuser => newuser === connectedUser);
        console.log(email, "  -> email is found in user list");
        
        next();
    } catch (error) {
        console.error('获取用户列表失败:', error);
        res.status(500).json({ error: '身份验证失败' });
    }
}

修改后,中间件会先完成异步身份验证逻辑,再触发后续的路由处理函数,执行顺序就能符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 09:20:28