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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 03:54:32