我遇到了一个非常奇怪的问题,即使经过数小时的故障排除,这个问题仍然存在。
首先,我的前端运行在 React+Vite 上(但我并没有真正使用任何与 Vite 相关的东西),托管在 Vercel 上,比如说,https://mywebsite.ca
我的后端在 Express 和 Node 上运行,也托管在 Vercel 上,https://backend.vercel.app
我的 MySQL 数据库托管在 AWS RDS 上
问题是登录或注册页面的所有内容都正常(有时它说不是服务器错误,但这种情况非常罕见)。在此阶段我没有遇到任何 CORS 问题。 然而,当用户登录时(引导我访问 https://mywebsite.ca/course-selection),我遇到了 CORS 问题。
我很确定我的 CORS 都定义良好,您可以在我的 index.js 中看到:
import express from "express"
import cors from "cors"
import userRoutes from "./routes/users.js"
import authRoutes from "./routes/auth.js"
import courseChangeRoutes from "./routes/courseChange.js"
import cookieParser from "cookie-parser";
const app = express();
app.use(express.json());
app.use(cookieParser());
app.use(
cors({
origin: 'https://www.mywebsite.ca/',
methods: ['GET', 'POST', 'OPTIONS', 'PUT', 'DELETE'],
credentials: true,
})
);
app.use((req, res, next) => {
res.header("Access-Control-Allow-Credentials", true);
next();
});
app.use("/backend/users", userRoutes);
app.use("/backend/auth", authRoutes);
app.use("/backend/courseChange", courseChangeRoutes);
我什至添加了选项,因为我做了一些研究,发现有时预检请求是使用选项发出的。然而问题仍然存在,我仍然经常收到: 从源“https://www.mywebsite.ca”访问“https://backend.vercel.app/backend/courseChange/”处的 XMLHttpRequest 已被 CORS 策略阻止:无“Access-Control-Allow-Origin”标头存在于所请求的资源上。 然后接着是 加载资源失败:net::ERR_FAILED
/operation/courseChange.js:(后端)
export const courseChange = async (req, res) => {
const { userId, courses_ids, term } = req.body;
if (!userId || !Array.isArray(courses_ids) || !term) {
return res.status(400).send('Invalid input');
}
const maxCourses = 15;
try {
// Clear existing courses for the specified term
let clearQuery = 'UPDATE users SET ';
for (let i = 1; i <= maxCourses; i++) {
clearQuery += `${term}Course${i} = NULL, `;
}
clearQuery = clearQuery.slice(0, -2) + ' WHERE id = ?';
console.log('Clear Query:', clearQuery, [userId]);
await db.execute(clearQuery, [userId]);
// Construct the update query for new courses
let updateQuery = 'UPDATE users SET ';
const updateValues = [];
if (courses_ids.length > 0) {
courses_ids.forEach((courseId, index) => {
updateQuery += `${term}Course${index + 1} = ?, `;
updateValues.push(courseId);
});
updateQuery = updateQuery.slice(0, -2);
updateQuery += ' WHERE id = ?';
updateValues.push(userId);
console.log('Update Query:', updateQuery, updateValues);
await db.execute(updateQuery, updateValues);
res.status(200).send('Courses updated successfully');
}
} catch (error) {
console.error('Error updating courses:', error);
res.status(500).send('Error updating courses: ' + error.message);
}
};
/pages/course-selection.jsx:(前端)
import { useState, useEffect } from 'react';
import LeftMenu from '/src/components/leftMenu';
import Calendar from '/src/components/calendar';
import Nav from '/src/components/nav';
import Selection from '/src/components/selections';
import fallJSON from '/src/assets/fall_2024_0626.json';
import winterJSON from '/src/assets/winter_2025_0626.json';
import axios from 'axios'
axios.defaults.withCredentials = true;
export default function Courses() {
const [fallCourses, setFallCourses] = useState([]);
const [winterCourses, setWinterCourses] = useState([]);
const [fallData, setFallData] = useState(fallJSON);
const [winterData, setWinterData] = useState(winterJSON);
const [err, setError] = useState(null);
const updateFallCourses = async (courses_ids) => {
// Prepare for Calendar Rendering
const courses = courses_ids.flatMap(course => fallData[course].slice(2));
setFallCourses(courses);
// Send data to backend
const user = JSON.parse(localStorage.getItem('user'));
const userId = user ? user.id : null;
const term = "fall"
try {
await axios.post('https://backend.vercel.app/backend/courseChange/', {
userId,
courses_ids,
term,
}, {
headers: {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json',
}
});
console.log('Courses updated successfully');
} catch (err) {
const errorMessage = err.response?.data?.message || "An unexpected error occurred";
setError(errorMessage);
}
};
// There's also const updateWinterCourses right here that basically is the same code
context/authContext.jsx(处理登录注销,完全有效)
import axios from "axios";
import { createContext, useEffect, useState } from "react";
export const AuthContext = createContext();
export const AuthContextProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(
JSON.parse(localStorage.getItem("user")) || null
);
const login = async (inputs) => {
const res = await axios.post("https://backend.vercel.app/backend/auth/login", inputs, {
withCredentials: true,
});
setCurrentUser(res.data)
};
const logout = async () => {
await axios.post("https://backend.vercel.app/backend/auth/logout", {}, {
withCredentials: true
});
setCurrentUser(null);
};
useEffect(() => {
localStorage.setItem("user", JSON.stringify(currentUser));
}, [currentUser]);
return (
<AuthContext.Provider value={{ currentUser, login, logout }}>
{children}
</AuthContext.Provider>
);
};
最后,一些意想不到的事情,在调用函数 updateFallCourses 时,可能会发生以下情况之一:
我尝试过的方法包括:允许连接index.js中的所有OPTIONS请求,但这并没有解决我的问题。另外,如果重要的话,当我在 http://localhost:8800 上运行后端并在 http://localhost:3000 上运行前端时,一切都会非常顺利,没有任何 CORS 问题(显然当时 CORS 中定义的源将是 localhost: 3000)。
我真的被困住了,希望在一两天内启动该网站。这是最后一步,因为无法更新用户对数据库的更改,我错过了最重要的功能。我将不胜感激任何建议!由于这是我的第一个全栈项目,如果您有有关如何管理不同端的相关提示,或者是否可以以更简洁的方式编写一些代码,我也将不胜感激!谢谢!!
您遇到的问题似乎与 CORS 设置有关,或者可能与处理您的请求的方式存在一些不一致有关。
以下是基于您的代码的建议,可帮助您排除故障并解决问题。
CORS 配置看起来正确,一个小问题是原始设置,它不应在原始 URL 中包含尾部斜杠。
app.use(
cors({
origin: 'https://www.mywebsite.ca', // Remove trailing slash
methods: ['GET', 'POST', 'OPTIONS', 'PUT', 'DELETE'],
credentials: true,
})
);
向日志标头添加一个中间件,这将帮助您查看响应是否具有正确的 CORS 标头,
app.use((req, res, next) => {
res.header("Access-Control-Allow-Credentials", true);
res.header("Access-Control-Allow-Origin", "https://www.mywebsite.ca"); // Explicitly set the allowed origin
res.header("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
console.log(`CORS headers set for ${req.method} ${req.url}`);
next();
});
确保服务器处理预检选项请求,
app.options('*', (req, res) => {
res.sendStatus(200);
});
在
updateFallCourses
函数中,不要设置 Access-Control-Allow-Origin
标头,删除该标头并仅保留 Content-Type
headers: {
'Content-Type': 'application/json',
}
Vercel 可能对处理 CORS 有一些特定的要求或设置。确保没有特定于平台的问题导致问题。
使用 Postman 等工具模拟对后端的请求并检查标头和响应。
通过执行以下步骤并确保所有配置设置正确,您应该能够诊断并修复 CORS 问题。