Express路由调用async控制器报Route.post()需回调却得到Promise错误如何解决
报错原因
你当前的代码存在两个核心问题直接触发报错:
wrapRoute没有接收要包装的控制器函数fn作为入参,逻辑本身不完整wrapRoute被定义为async函数,你直接调用wrapRoute(async (req,res)=>{})后返回的是Promise对象,而非Express路由要求的回调函数,因此触发Route.post() requires a callback function but got a [object Promise]报错。
修复方案
第一步:修正wrapRoute的实现
将wrapRoute改造为高阶函数,接收目标控制器函数作为入参,返回符合Express要求的中间件回调函数:
// 调整为接收fn作为参数,返回async中间件函数 const wrapRoute = (fn) => async (req, res, next) => { try { await fn(req, res, next) } catch (e) { next(e) } }
第二步:补充遗漏的依赖引入
你当前EmailLogin.js里缺少必要依赖的引入,修复完路由问题后运行也会报错,在文件顶部补充对应引入即可:
const axios = require('axios'); const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); const Users = require('../models/Users'); // 替换为你自己的User模型实际路径 const env = require('../config/env'); // 替换为你自己的环境变量配置实际路径
第三步:挂载全局错误处理中间件
你定义的errorHandler当前没有挂载到Express实例上,捕获的异常无法正常处理,调整App.js代码:
var express = require("express"); var app = express(); const { errorHandler } = require('./controllers/EmailLogin'); // 替换为对应实际路径 const Router = require('./routes/Router'); app.use('/', Router); // 全局错误处理中间件必须放在所有路由挂载之后 app.use(errorHandler); app.listen(3000, function() { console.log('listening on 3000'); });
同时在EmailLogin.js里导出errorHandler:
module.exports = {EmailLogin, errorHandler};
常见疑问解答
Express原生不支持直接传入Promise作为路由处理函数,必须传入可调用的函数。你可以通过上述高阶函数包装的方式,让async函数(执行后返回Promise)作为路由处理函数使用,所有异步抛出的异常都会被wrapRoute捕获后传递给全局错误中间件处理,不需要每个控制器单独编写try catch逻辑。
可选优化:统一异步写法避免回调嵌套
你当前代码同时混用了await、then、回调函数三种异步写法,容易出现回调内异常无法被外层捕获的问题,建议统一用async/await改写控制器逻辑:
const EmailLogin = wrapRoute(async (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(422).json({ errors: errors.array() }); } const gtoken = req.body.gtoken; const gSecretKey = env.secret_key; if (!gtoken) throw new Error('no token') const captchaURL = `https://www.google.com/recaptcha/api/siteverify?secret=${gSecretKey}&response=${gtoken}` const {data: gVerifyData} = await axios.post(captchaURL, {}, { headers: {ContentType: 'application/x-www-form-urlencoded'}, }) if (!gVerifyData.success) { return res.status(500).send('bot'); } const user = await Users.findOne({'email': req.body.email}).select('+hashPassword +status') if (!user) { return res.send("wrong-info"); } const validPassword = bcrypt.compareSync(req.body.password, user.hashPassword); if (!validPassword || user.status !== "active"){ return res.send("wrong-info"); } const token = jwt.sign({ id: user._id }, env.jwtsecret, { expiresIn: "168h" }); res.status(200).send({ token: token, user }); });
内容的提问来源于stack exchange,提问作者turkdev
相关产品推荐
相关产品推荐

