Next.js部署Vercel/Heroku时createAsyncThunk axios POST报504错误
问题根因与解决方法
这个问题和部署平台(Vercel/Heroku)无关,核心是代码写法存在两个关键问题,刚好在开发环境因为Next.js本地服务的容错机制没有暴露:
1. 最直接原因:混淆axios与fetch的API语法,请求体结构异常
你现在的axios写法完全照搬了fetch的参数格式,属于语法错误:
// 错误写法:把fetch的配置对象当成了axios的请求体 axios.post('/api/adminlogin', { method: 'POST', data: initialCred, })
axios的post方法语法是axios.post(请求地址, 请求体, 配置项),第二个参数会被直接序列化为请求体发送,你现在相当于给后端发的请求体长这样:
{ "method": "POST", "data": {"username": "xxx", "password": "xxx"} }
开发环境下Next.js本地服务的body-parser容错性高,可能自动做了解析兼容,但生产环境Serverless函数的解析逻辑更严格,异常嵌套的请求体可能触发解析器挂起,最终导致超时返回504。而你用Postman调试时,大概率是直接传的扁平结构{username, password},所以能正常响应。
修复方法
把thunk里的请求改成axios标准写法,同时增加错误捕获方便定位问题:
export const validateAdmin = createAsyncThunk( 'Admin/validateAdmin', async (initialCred, {rejectWithValue}) => { try { const res = await axios.post('/api/adminlogin', initialCred) return res.data } catch (err) { return rejectWithValue(err.response?.data || err.message) } } )
同时修改API路由里的取值逻辑,去掉多余的.data层级(因为现在请求体直接就是账号密码对象):
// 原来的 req.body.data.username 改成 req.body.username const admin_user = await Admin.findOne({ username: req.body.username, }); // 后续所有 req.body.data 都替换成 req.body if ( req.body.username == admin_user.username && req.body.password == decryptedPass ) // 其他报错提示里的 req.body.data.username 也同步替换
2. 潜在问题:Serverless环境下数据库连接未做缓存,导致连接挂起
Vercel、Heroku的服务都是Serverless/容器化运行,每次请求可能触发冷启动,如果你的connectDB中间件每次请求都新建MongoDB连接,没有做全局缓存,很容易触发数据库连接数限制,导致查询操作挂起超时,这也是你跨两个平台都出现504的次要原因。
修复方法
修改你的mongoose连接中间件,增加连接缓存逻辑,参考写法:
import mongoose from 'mongoose' const MONGODB_URI = process.env.MONGODB_URI // 全局缓存连接 let cached = global.mongoose if (!cached) { cached = global.mongoose = { conn: null, promise: null } } async function connectDB(handler) { return async (req, res) => { if (cached.conn) { return handler(req, res) } if (!cached.promise) { const opts = { bufferCommands: false, } cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => { return mongoose }) } cached.conn = await cached.promise return handler(req, res) } } export default connectDB
3. 补充修复点
- 去掉package.json里配置的proxy字段:Next.js本身不识别这个字段,只有CRA类项目才会用到,多余的代理配置可能在生产环境导致请求路径解析异常
- API路由里给非POST方法返回405状态码,同时补全CORS头,避免OPTIONS预检请求挂起:
// 在handler开头加CORS处理 res.setHeader('Access-Control-Allow-Credentials', true) res.setHeader('Access-Control-Allow-Origin', '*') res.setHeader('Access-Control-Allow-Methods', 'GET,OPTIONS,PATCH,DELETE,POST,PUT') res.setHeader( 'Access-Control-Allow-Headers', 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version' ) if (req.method === 'OPTIONS') { res.status(200).end() return } - 提交表单时给thunk加错误处理,避免静默失败:
const handleSubmit = async (e) => { e.preventDefault(); try { await dispatch(validateAdmin({ username, password })).unwrap() // 登录成功后的跳转逻辑 } catch (err) { console.error('登录失败', err) } };
内容的提问来源于stack exchange,提问作者Hassan Aftab
相关产品推荐
相关产品推荐

