Heroku部署API请求出现/undefined/api前缀异常求助
排查Heroku部署后请求路径出现/undefined/api/clinics的问题
问题背景
部署应用到Heroku后,日志中发现异常请求路径:/undefined/api/clinics。代码中未在api前缀前定义任何变量,Heroku配置仅包含ACCESS_TOKEN_SECRET、MONGODB_URI和REFRESH_TOKEN_SECRET,数据库连接正常(用户可完成登录、登出操作)。直接访问/api/clinics可正常获取诊所数据。
前端代码(获取诊所数据)
useEffect(() => { const fetchClinics = async () => { try { const response = await axios.get('https://dental-w-032fe80aafac.herokuapp.com/api/clinics'); setClinics(response.data) } catch (error) { console.error(error) } } fetchClinics() }, [])
后端相关代码
index.js
app.use('/api', require('./routes/authRouter')); const client = new MongoClient(process.env.MONGODB_URI); app.use(express.static(buildPath)); // create storage engine const storage = new GridFsStorage({ url: process.env.MONGODB_URI, file: (req, file) => { return { filename: `${Date.now()}${path.extname(file.originalname)}` }; } }); const upload = multer({ storage }); app.get('/', (req, res) => { res.json({ message: 'Welcome to the server' }); }); // Multer Middleware for file upload app.post('/api/clinics', upload.single('image'), (req, res) => { const rating = Number((Math.random() * (5 - 3.5) + 3.5).toFixed(1)) let newClinic = new Clinic({ name: req.body.name, address: req.body.address, doctor: req.body.doctor, price: req.body.price, rating, image: { // store file data id: req.file.id, contentType: req.file.contentType, filename: req.file.filename } }); newClinic.save() .then(clinic => res.json(clinic)) .catch(err => res.status(400).json('Error:' + err)); }); // Route for serving images app.get('/api/image/:filename', async (req, res) => { const { filename } = req.params; let image = imageCache.get(filename); // Load from the cache if possible if (image) { res.setHeader('Content-Type', image.contentType); res.send(image.buffer); return; } try { await client.connect(); const db = client.db("test"); const bucket = new GridFSBucket(db, { bucketName: "fs" }); const downloadStream = bucket.openDownloadStreamByName(filename); let chunks = []; downloadStream.on('data', (chunk) => { chunks.push(chunk); }); downloadStream.on('end', () => { let buffer = Buffer.concat(chunks); let imageObject = { contentType: 'image/jpeg', buffer }; // Cache the image imageCache.set(filename, imageObject); res.setHeader('Content-Type', imageObject.contentType); res.send(buffer); }); } catch (err) { console.error(err); res.status(404).send('Image not found'); } });
routes/authRouter.js
// routes/authRouter.js router.get('/clinics', authCtrl.getClinics); router.post('/clinics', authCtrl.createClinics);
authCtrl.js
const Clinic = require('../models/clinicsModel') const authCtrl = { getClinics : async (req, res) => { try { const clinics = await Clinic.find().sort({ createdAt: -1 }); res.json(clinics); } catch (error) { console.error(error); res.status(500).send('An error occurred while fetching the clinics.'); } }, createClinics : async (req, res) => { try { const {name, address, medicalWorker} = req.body; if (!name || !address || !medicalWorker) { return res.status(400).json({message: 'Bad request: name, medicalWorker, and address are required'}); } const image = req.file ? req.file.filename : null; const newClinic = new Clinic({name, medicalWorker, address, image}); const savedClinic = await newClinic.save(); res.json(savedClinic); } catch (error) { console.error(error); res.status(500).json({message: 'Server error. Please try again later.'}); } }, }
已尝试的解决方案
- 将请求URL改为相对路径
- 调整CORS设置
- 配置前端proxy:
{ "name": "client", "version": "0.1.0", "proxy": "https://dental-w-032fe80aafac.herokuapp.com", "private": true, ... }
排查思路与解决建议
检查环境变量配置
- 确认前端是否使用了环境变量拼接API URL,比如
${process.env.REACT_APP_API_URL}/api/clinics。如果REACT_APP_API_URL未在Heroku的前端环境变量中配置,会导致拼接出undefined/api/clinics。 - 若使用Create React App,需确保环境变量以
REACT_APP_开头,并在Heroku的应用设置中添加对应变量。
- 确认前端是否使用了环境变量拼接API URL,比如
检查axios全局配置
- 查看是否在前端代码中设置了axios的
baseURL,比如:
如果该环境变量未定义,所有请求都会带上axios.defaults.baseURL = process.env.REACT_APP_API_BASE;undefined前缀。
- 查看是否在前端代码中设置了axios的
检查请求拦截器
- 确认是否存在axios请求拦截器,是否在拦截器中错误修改了请求URL,比如拼接了未赋值的变量。
查看浏览器Network日志
- 在浏览器开发者工具的Network标签中,找到发送
/undefined/api/clinics的请求,查看请求的发起源(Initiator),确认是哪个组件或脚本发送的请求,定位代码问题。
- 在浏览器开发者工具的Network标签中,找到发送
检查打包后的代码
- 查看前端打包后的静态文件,搜索
undefined/api/clinics,确认URL是如何被错误生成的。
- 查看前端打包后的静态文件,搜索
内容的提问来源于stack exchange,提问作者Temirlan
相关产品推荐
相关产品推荐

