Nextjs API转发FormData至Express后端报405错误(本地正常线上失效)
问题:Next.js API转发音频到Express后端生产环境报405错误
已解决 / 详情见本人回答
用户在Next.js前端录制的音频可正常发送到Next.js API,但将该音频从Next.js API转发到Vercel上的Express.js后端时,出现405错误请求。
重要说明
- 本地环境一切正常,但部署到Vercel生产环境后失效(客户端和服务端均为Vercel上的Node应用,一个是旧版Next.js,另一个是Express)
- 出于安全考虑,使用Next.js作为中间层API
问题推测
目前推测是formData.append工作异常,因为无论如何打印FormData或其条目,得到的都是空对象{}。
相关代码片段:
const formDataToSend = new FormData() formDataToSend.append(fileName, new Blob([readResult], { type: 'audio/wav' }), `${fileName}.wav`)
版本信息
- Next.js版本:13.5.4
- Express.js版本:4.18.2
Express.js后端代码
app.use(cors()) const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, '/tmp/') }, filename: function (req, file, cb) { cb(null, `${file.originalname}`) } }) const upload = multer({ storage: storage }) // ... app.post('/upload', [requireSession, upload.any()], async (req, res) => { // 请求根本无法到达这里 // requireSession工作正常 })
Next.js中间层API代码
import { csrf } from '../../../../lib/csrf' import axios from 'axios' import crypto from 'crypto' import multiparty from 'multiparty' import fs from 'fs' // 有效URL const ENDPOINT_URL = 'https://my-api.com/upload' // 工作正常 async function readAudioFile(filePath) { return new Promise((resolve, reject) => { const readStream = fs.createReadStream(filePath) const chunks = [] readStream.on('data', (chunk) => { chunks.push(chunk) }) readStream.on('end', () => { const fileBuffer = Buffer.concat(chunks) resolve(fileBuffer) }) readStream.on('error', (err) => { reject(err) }) }) } async function post(req, res) { try { // 工作正常 const form = new multiparty.Form() // 工作正常 const formData = await new Promise((resolve, reject) => { form.parse(req, function (err, fields, files) { if (err) reject({ err }) resolve({ fields, files }) }) }) // 工作正常 const readResult = await readAudioFile(formData.files.audio[0].path) // 工作正常 const randomizer = `${req?.headers?._uuid}-${req?.query?.taskId}-${(new Date().getTime()).toString(16)}` // 工作正常 const fileName = crypto.createHash('sha256').update(randomizer).digest('hex') // 工作正常 const formDataToSend = new FormData() // !! 不确定。打印时,"formDataToSend"和"formDataToSend.entries()"均为空对象{} formDataToSend.append(fileName, new Blob([readResult], { type: 'audio/wav' }), `${fileName}.wav`) // 此处无未定义项,工作正常 const headers = { 'client': process.env.API_ACCESS_KEY, '_session': req.headers._session, '_uuid': req.headers._uuid, ...req.headers, 'Content-Type': 'multipart/form-data', 'Request-Index': '0' , } // 此请求失败,返回405错误 await axios.post(ENDPOINT_URL, formDataToSend, { headers, params: { ...req.query } }) .then(response => (res.status(200).json(response.data))) .catch(error => (console.log(error), res.status(500).json({ ...getErrorMessage(), errorDetails: error, dataToSend }))) } catch (error) { console.log(error) res.status(501).json({ ...getErrorMessage(), errorDetails: error, dataToSend }) } } async function handler(req, res) { switch (req.method.toLowerCase()) { case 'post': await post(req, res) break default: res.status(200).send(`Method ${req.method} is not allowed.`) break } } const getErrorMessage = () => { return { time: Date.now(), result: false, status: 500, message: 'Something went wrong' } } export default csrf(handler) export const config = { api: { bodyParser: false } }
已尝试的方案
- 用fetch替代axios发送请求
- 调整Express.js端点的CORS设置
内容的提问来源于stack exchange,提问作者Jason Myers
相关产品推荐
相关产品推荐

