Node.js+MySQL全栈项目部署后CORS及503错误求助
将基于Node.js、React、MySQL的全栈项目部署至生产环境,后端部署到Railway、前端部署到Netlify后,前端出现以下错误:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://mysql-------railway.app/films. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 503.
直接访问后端域名显示Railway错误页,提示“Application failed to respond”。已检查所有环境变量并添加CORS相关代码,但问题仍未解决,附后端及前端代码求解决办法。
后端代码(server/index.js)
import express from "express"; import mysql from "mysql"; import cors from "cors"; import dotenv from "dotenv"; dotenv.config(); const app = express(); app.use( cors({ origin: "---frontend domain---", methods: ["GET", "POST", "PUT", "DELETE"], allowedHeaders: ["Content-Type"], credentials: true, }) ); app.options("*", cors()); const urlDB = `mysql://${process.env.MYSQLUSER}:${process.env.MYSQLPASSWORD}@${process.env.MYSQLHOST}:${process.env.MYSQLPORT}/${process.env.MYSQLDATABASE}`; const db = mysql.createConnection(urlDB); app.use(express.json()); app.use(cors()); app.get("/films", (req, res) => { const q = "SELECT * FROM films"; db.query(q, (err, data) => { if (err) return res.json(err); return res.json(data); }); }); app.post("/films", (req, res) => { const q = "INSERT INTO films(`title`, `plot`, `poster`, `rating`, `release_year`) VALUES (?)"; const values = [ req.body.title, req.body.plot, req.body.poster, req.body.rating, req.body.release_year, ]; db.query(q, [values], (err, data) => { if (err) return res.json(err); return res.json("Film has been added successfully"); }); }); app.delete("/films/:id", (req, res) => { const filmId = req.params.id; const q = "DELETE FROM films WHERE id = ?"; db.query(q, [filmId], (err, data) => { if (err) return res.json(err); return res.json("Film has been removed successfully"); }); }); app.put("/films/:id", (req, res) => { const filmId = req.params.id; const q = "UPDATE films SET `title` = ?, `plot` = ?, `poster` = ?, `rating` = ?, `release_year` = ? WHERE id = ?"; const values = [ req.body.title, req.body.plot, req.body.poster, req.body.rating, req.body.release_year, ]; db.query(q, [...values, filmId], (err, data) => { if (err) return res.json(err); return res.json("Film has been updated successfully"); }); }); const PORT = process.env.MYSQLPORT; app.listen(PORT, () => { console.log("Connected to backend!"); });
前端代码(client/index.js)
import React from "react"; import { useEffect } from "react"; import { useState } from "react"; import axios from "axios"; import { Link } from "react-router-dom"; const Films = () => { const [films, setFilms] = useState([]); useEffect(() => { const fetchAllMovies = async () => { try { const res = await axios.get(`${process.env.REACT_APP_MYSQL_URL}/films`); setFilms(res.data); } catch (err) { console.log(err); } }; fetchAllMovies(); console.log(films); }, []); const handleDelete = async (id) => { try { await axios.delete(`${process.env.REACT_APP_MYSQL_URL}/films/` + id); window.location.reload(); } catch (err) { console.log(err); } }; return ( <div> <h1>Films</h1> <div> {films.map((film) => ( <div key={film.id}> <h2>{film.title}</h2> {film.poster === null ? ( <> </> ) : ( <img width={50} height={80} src={film.poster} /> )} <p>{film.plot}</p> <p>{film.release_year}</p> <p>{film.rating}</p> <button onClick={() => handleDelete(film.id)}>Delete movie</button> <button> <Link to={`/update/${film.id}`}>Update</Link> </button> </div> ))} </div> </div> ); }; export default Films;
1. 修复后端端口配置错误
当前用process.env.MYSQLPORT作为后端服务监听端口,这是数据库端口(默认3306),Railway部署的后端需要监听平台分配的端口,该端口存在process.env.PORT变量中:
// 替换原端口定义 const PORT = process.env.PORT || 3001; // 本地开发用3001,生产环境用Railway分配的端口
2. 移除重复的CORS配置
代码中先后调用了两次app.use(cors()),第二次无参数的默认配置会覆盖第一次的自定义规则,导致origin等设置失效,删除第二次的app.use(cors());即可。
3. 添加数据库连接错误处理
当前代码未处理数据库连接失败的情况,若数据库连接失败会导致后端服务无响应,添加连接回调:
const db = mysql.createConnection(urlDB); db.connect((err) => { if (err) { console.error('数据库连接失败:', err); return; } console.log('数据库连接成功'); });
4. 验证环境变量配置
- Railway后端:确认
MYSQLUSER、MYSQLPASSWORD、MYSQLHOST、MYSQLPORT、MYSQLDATABASE完全匹配Railway提供的MySQL实例信息;origin配置中的前端域名需替换为Netlify分配的实际域名。 - Netlify前端:确认
REACT_APP_MYSQL_URL设置为Railway后端的完整域名(如https://mysql-------railway.app),不要加结尾斜杠。
5. 测试后端服务
修改代码重新部署到Railway后,直接访问https://your-backend-domain/films,若返回JSON数据或数据库错误信息,说明后端已正常响应;若仍显示Railway错误页,查看Railway日志排查启动报错(如端口占用、数据库连接失败)。
内容的提问来源于stack exchange,提问作者queercoded

