部署到Heroku后后端无法运行的技术求助
问题描述
- 应用部署到Heroku后后端无法正常运行,控制台出现axios错误
- 本地运行相同代码无异常,仅能访问前端首页,无法进入其他页面
- 已配置
app.listen(process.env.PORT || 8000),调整过app.listen位置,也添加了MongoDB URL配置变量,问题仍未解决
后端主文件 index.js
const express = require('express') const {MongoClient} = require('mongodb') const {v4: uuidv4} = require('uuid') const jwt = require('jsonwebtoken') const cors = require('cors') const bcrypt = require('bcrypt') require('dotenv').config() const uri = process.env.MONGODB_URL const app = express() app.use(cors()) app.use(express.json()) // const PORT = process.env.PORT || 8000 //----------------------------------- SIGNUP ----------------------------------- app.post('/signup', async (req, res) => { const client = new MongoClient(uri) const {email, password} = req.body const generateduserId = uuidv4() const hashedPassword = await bcrypt.hash(password, 10) //learn more about hashed passwords try { await client.connect() const database = client.db('app-data') const users = database.collection('users') //check user exists const existingUser = await users.findOne({email}) if (existingUser) { return res.status(409).send('user already exists, please login') } const sanitizedEmail = email.toLowerCase() //configuring data before adding to database const data = { user_id: generateduserId, email: sanitizedEmail, hashed_password: hashedPassword } const insertedUser = await users.insertOne(data) //inserts user (data) to database //insertedUser and sanitizedEmail are required values to generate a token const token = jwt.sign(insertedUser, sanitizedEmail, { expiresIn: 60 * 24, //expires in 24 hours }) res.status(201).json({token, userId: generateduserId}) } catch (error) { console.log(error) } }) //----------------------------------- LOGIN ----------------------------------- app.post('/login', async (req, res) => { const client = new MongoClient(uri) const {email, password} = req.body try { await client.connect() const database = client.db('app-data') const users = database.collection('users') const user = await users.findOne({email}) const correctPassword = await bcrypt.compare(password, user.hashed_password) if (user && correctPassword) { const token = jwt.sign(user, email, { expiresIn: 60 * 24, //expires in 24 hours }) res.status(201).json({token, userId: user.user_id}) } else { res.status(400).send('invalid credentials') } } catch (error) { console.log(error) } }) //----------------------------------- UPDATING ----------------------------------- app.put('/user', async (req, res) => { const client = new MongoClient(uri) const formData = req.body.formData try { await client.connect() const database = client.db('app-data') const users = database.collection('users') const query = {user_id: formData.user_id} const updateDocument = { $set: { first_name: formData.first_name, dob_day: formData.dob_day, dob_month: formData.dob_month, dob_year: formData.dob_year, show_gender: formData.show_gender, gender_identity: formData.gender_identity, gender_interest: formData.gender_interest, url: formData.url, about: formData.about, matches: formData.matches }, } const insertedUser = await users.updateOne(query, updateDocument) res.send(insertedUser) } finally { await client.close() //ensures client will close when finished or error occurs } }) //----------------------------------- DASHBOARD ----------------------------------- app.get('/user', async (req, res) => { const client = new MongoClient(uri) const userId = req.query.userId try { await client.connect() const database = client.db('app-data') const users = database.collection('users') const query = {user_id: userId} const user = await users.findOne(query) res.send(user) } finally { await client.close() } }) //ONLY DISPLAY THE GENDER THAT USER WANTS app.get('/gendered-users', async (req, res) => { const client = new MongoClient(uri) const gender = req.query.gender try { await client.connect() const database = client.db('app-data') const users = database.collection('users') const query = { gender_identity: {$eq: gender} } const foundUsers = await users.find(query).toArray() //return users with genders are interested in res.send(foundUsers) } finally { await client.close() //ensures client will close when finished or error occurs } }) //ADD MATCHES TO USER DATABASE app.put('/addmatch', async (req, res) => { const client = new MongoClient(uri) const {userId, matchedUserId} = req.body try { await client.connect() const database = client.db('app-data') const users = database.collection('users') const query = {user_id: userId} const firstUpdateDocument = { $push: {matches: {user_id: matchedUserId}} } const firstMatchesUpload = await users.updateOne(query, firstUpdateDocument) const userMatches = await users.findOne({user_id: userId}) //remove duplicates const jsonObj = userMatches.matches.map(JSON.stringify) const uniqueSet = new Set(jsonObj) const uniqueArray = Array.from(uniqueSet).map(JSON.parse) const user = await users.updateOne(query, {$set: {matches: uniqueArray}}) res.send(user) } finally { await client.close() } }) //SHOW MATCHED USERS IN MATCHES DISPLAY app.get('/users', async (req, res) => { const client = new MongoClient(uri) const userIds = JSON.parse(req.query.userIds) try { await client.connect() const database = client.db('app-data') const users = database.collection('users') const pipeline = [ { '$match': { 'user_id': { '$in': userIds } } } ] const foundUsers = await users.aggregate(pipeline).toArray() res.send(foundUsers) } finally { await client.close() } }) //----------------------------------- CHAT ----------------------------------- app.get('/messages', async (req, res) => { const client = new MongoClient(uri) const {userId, correspondingUserId} = req.query try { await client.connect() const database = client.db('app-data') const messages = database.collection('messages') const query = { from_userId: userId, to_userId: correspondingUserId } const foundMessages = await messages.find(query).toArray() res.send(foundMessages) } finally { await client.close() } }) app.post('/message', async (req, res) => { const client = new MongoClient(uri) const message = req.body.message try { await client.connect() const database = client.db('app-data') const messages = database.collection('messages') const insertedMessage = await messages.insertOne(message) res.send(insertedMessage) } finally { await client.close() } }) app.use(express.static(__dirname + '/client/build')) app.get('*', (req, res) => { res.sendFile(__dirname + '/client/build/index.html') }) app.listen(process.env.PORT || 8000)
错误触发文件 AuthModal.js
import {useState} from 'react' import axios from 'axios' import {useNavigate} from 'react-router-dom' import {useCookies} from'react-cookie' const AuthModal = ({setShowModal, isSignUp}) => { const [email, setEmail] = useState(null) const [password, setPassword] = useState(null) const [confirmPassword, setConfirmPassword] = useState(null) const [error, setError] = useState(null) const [cookies, setCookie, removeCookie] = useCookies(['user']) let navigate = useNavigate() isSignUp ? console.log(email, password, confirmPassword) : console.log(email, password) const handleClick = () => { setShowModal(false) } const handleSubmit = async (e) => { e.preventDefault() //prevents page from refreshing try { if (isSignUp && (password !== confirmPassword)) { setError('Passwords do not match') return } // posts the email and password to the signup in backend or to login depending const response = await axios.post(`http://localhost:8000/${isSignUp ? 'signup': 'login'}`, {email, password}) console.log('response', response) //these are the parameters returned from the jsonwebtoken response status code setCookie('AuthToken', response.data.token) setCookie('UserId', response.data.userId) const success = response.status === 201 if (success && isSignUp) navigate ('/onboarding') //if post request successful and sigin up, navigate to onboarding page if (success && !isSignUp) navigate ('/dashboard') //if post request successful and loggin in, navigate to dashboard page window.location.reload() //make sure auth token is read by /onboarding and /dashboard } catch (error) { console.log(error) } } return ( <div className="auth_modal"> <div className='close_icon' onClick={handleClick}>✕</div> <h2>{isSignUp ? 'CREATE ACCOUNT' : 'LOG IN'}</h2> <p>By Logging In you agree to our T&Cs</p> <form onSubmit={handleSubmit}> <input type='email' id='email' name='email' placeholder='email' required={true} onChange={(e) => setEmail(e.target.value)} /> <input type='password' id='password' name='password' placeholder='password' required={true} onChange={(e) => setPassword(e.target.value)} /> {isSignUp && <input type='password' id='password-check' name='password-check' placeholder='confirm password' required={true} onChange={(e) => setConfirmPassword(e.target.value)} /> } <input className='secondary_button' type='submit' /> <p>{error}</p> </form> <hr/> <h2>GET THE APP</h2> </div> ) } export default AuthModal
后端package.json
{ "name": "server", "version": "1.0.0", "engines": { "node": "18.9.1" }, "description": "", "main": "index.js", "scripts": { "start": "node index.js", "heroku-postbuild": "cd client && npm i && npm run build" }, "author": "", "license": "ISC", "dependencies": { "bcrypt": "^5.1.0", "cors": "^2.8.5", "dotenv": "^16.0.3", "express": "^4.18.2", "jsonwebtoken": "^9.0.0", "mongodb": "^4.13.0", "nodemon": "^2.0.20", "uuid": "^9.0.0" } }
排查建议
- 前端请求地址问题:AuthModal.js中硬编码了
http://localhost:8000作为请求地址,部署到Heroku后前端仍会向本地8000端口发请求,导致axios错误。应改为相对路径,比如/${isSignUp ? 'signup': 'login'},这样会自动使用当前页面的域名和端口发送请求。 - 检查Heroku日志:通过
heroku logs --tail命令查看详细错误日志,确认后端是否有连接MongoDB失败、端口监听异常等问题。 - CORS配置优化:当前CORS配置为允许所有跨域请求,可尝试指定具体的前端域名,避免潜在的跨域问题。
- MongoDB连接验证:确认Heroku配置的
MONGODB_URL是否正确,可在本地使用该URL测试连接是否正常。 - 端口监听位置:确保
app.listen在所有路由定义之后,当前代码位置正确,但可尝试添加监听成功的日志:
便于确认服务是否正常启动。app.listen(process.env.PORT || 8000, () => { console.log(`Server running on port ${process.env.PORT || 8000}`) })
内容的提问来源于stack exchange,提问作者Edris Elbow
相关产品推荐
相关产品推荐

