React(Vite)请求Node.js后端登录接口遇CORS跨域错误求助
解决Vite React前端与Node.js后端的CORS跨域问题
问题概述
使用Vite搭建的React前端向基于Node.js+MongoDB的后端发送登录POST请求时,触发CORS跨域错误,提示预检请求未通过,目标资源缺少Access-Control-Allow-Origin响应头。
错误信息
login:1 Access to XMLHttpRequest at 'http://localhost:8800/api/auth/login' from origin 'http://127.0.0.1:5173' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
{ "message": "Network Error", "name": "AxiosError", "stack": "AxiosError: Network Error\n at XMLHttpRequest.handleError (http://127.0.0.1:5173/node_modules/.vite/deps/axios.js?v=b0cda0cd:1440:14)", "config": { "transitional": { "silentJSONParsing": true, "forcedJSONParsing": true, "clarifyTimeoutError": false }, "adapter": [ "xhr", "http" ], "transformRequest": [ null ], "transformResponse": [ null ], "timeout": 0, "xsrfCookieName": "XSRF-TOKEN", "xsrfHeaderName": "X-XSRF-TOKEN", "maxContentLength": -1, "maxBodyLength": -1, "env": {}, "headers": { "Accept": "application/json, text/plain, */*", "Content-Type": "application/json" }, "withCredentials": true, "method": "post", "url": "http://localhost:8800/api/auth/login", "data": "{\"username\":\"as\",\"password\":\"as\"}" }, "code": "ERR_NETWORK", "status": null }
POST http://localhost:8800/api/auth/login net::ERR_FAILED
相关代码
前端Login组件
const Login = () => { const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(false); const handleSubmit = async (e) => { e.preventDefault(); try { const res = await axios.post( "http://localhost:8800/api/auth/login", { username, password, }, { withCredentials: true, } ); console.log(res); } catch (err) { setError(err); console.log(error); console.log(err); } }; return ( <div className="login"> <form onSubmit={handleSubmit}> <h1>Sign in</h1> <label htmlFor="">Username</label> <input type="text" name="username" placeholder="ahmed" onChange={(e) => setUsername(e.target.value)} /> <label htmlFor="">Password</label> <input type="password" name="password" placeholder="password" onChange={(e) => setPassword(e.target.value)} /> <button type="submit">Login</button> </form> </div> ); };
后端server.js
const express = require("express"); const mongoose = require("mongoose"); const cookieParser = require("cookie-parser"); const dotenv = require("dotenv"); const userRoute = require("./routes/user.route.js"); const gigRoute = require("./routes/gig.route.js"); const orderRoute = require("./routes/order.route.js"); const messageRoute = require("./routes/message.route.js"); const conversationRoute = require("./routes/conversation.route.js"); const reviewRoute = require("./routes/review.route.js"); const authRoute = require("./routes/auth.route"); const cors = require("cors"); const app = express(); app.use(express.json()); app.use(cookieParser()); dotenv.config(); mongoose.set("strictQuery", true); function handleError(error) { console.error("An error occurred:", error); } async function startServer() { try { await mongoose.connect(process.env.MONGO); console.log("conneted!"); } catch (error) { handleError(error); } } app.use("/api/users", userRoute); app.use("/api/auth", authRoute); // app.use("/api/gigs", gigRoute); // app.use("/api/orders", orderRoute); // app.use("/api/conversations", conversationRoute); // app.use("/api/messages", messageRoute); // app.use("/api/reviews", reviewRoute); // error handling middleware app.use((err, req, res, next) => { const errorStatus = err.status || 500; const errorMessage = err.message || "Something went wrong!"; return res.status(errorStatus).send(errorMessage); }); // app.use( //here i am using cors cors({ origin: "*", }) ); app.listen(8800, () => { startServer(); console.log("app is listening at 8800!"); });
后端auth.route.js
const express = require("express"); const { register, login, logout } = require("../controllers/auth.controller"); const router = express.Router(); router.post("/register", register); router.post("/login", login); router.post("/logout", logout); module.exports = router;
后端auth.controller.js
const User = require("../models/user.model"); const bcrypt = require("bcrypt"); const jwt = require("jsonwebtoken"); const { createError } = require("../utils/createError"); const login = async (req, res, next) => { try { const user = await User.findOne({ username: req.body.username }); if (!user) return next(createError(404, "user not found!")); const isCorrect = bcrypt.compareSync(req.body.password, user.password); if (!isCorrect) return next(createError(400, "Wrong password or username!")); const token = jwt.sign( { id: user._id, //object id isSeller: user.isSeller, }, process.env.JWT_KEY ); const { password, ...info } = user._doc; res.header("Access-Control-Allow-Origin", "http://127.0.0.1:5173"); res .cookie("accessToken", token, { httpOnly: true, }) .status(200) .send(info); res.status(200).send(user); } catch (err) { next(err); } }; const logout = async (req, res) => { res .clearCookie("accessToken", { sameSite: "none", secure: true, }) .status(200) .send("User has been logged out!"); }; const register = async (req, res, next) => { try { const hash = bcrypt.hashSync(req.body.password, 5); const newUser = new User({ ...req.body, password: hash }); await newUser.save(); res.status(201).send("User has been created"); } catch (err) { next(err); } }; module.exports = { login, logout, register, };
修复方案
1. 调整CORS中间件顺序
Express中间件按注册顺序执行,CORS中间件必须放在路由和错误处理中间件之前,否则预检请求(OPTIONS)无法被正确处理。修改server.js:
// 先注册CORS中间件 app.use( cors({ origin: "http://127.0.0.1:5173", // 匹配前端地址 credentials: true, // 允许携带Cookie }) ); // 再注册路由 app.use("/api/users", userRoute); app.use("/api/auth", authRoute); // 最后注册错误处理中间件 app.use((err, req, res, next) => { const errorStatus = err.status || 500; const errorMessage = err.message || "Something went wrong!"; return res.status(errorStatus).send(errorMessage); });
2. 移除重复的响应发送与冗余Header设置
在auth.controller.js的login函数中,res.cookie(...).send(info)已经发送了响应,后面的res.status(200).send(user)会导致"Cannot set headers after they are sent to the client"错误,同时手动设置的CORS Header可由中间件统一处理,直接删除冗余代码:
const login = async (req, res, next) => { try { const user = await User.findOne({ username: req.body.username }); if (!user) return next(createError(404, "user not found!")); const isCorrect = bcrypt.compareSync(req.body.password, user.password); if (!isCorrect) return next(createError(400, "Wrong password or username!")); const token = jwt.sign( { id: user._id, isSeller: user.isSeller, }, process.env.JWT_KEY ); const { password, ...info } = user._doc; // 移除手动设置的CORS Header,由中间件统一处理 // res.header("Access-Control-Allow-Origin", "http://127.0.0.1:5173"); res .cookie("accessToken", token, { httpOnly: true, // 开发环境可省略sameSite和secure,生产环境需配置 // sameSite: "none", // secure: true, }) .status(200) .send(info); // 删除重复的响应发送 // res.status(200).send(user); } catch (err) { next(err); } };
3. 可选:使用环境变量管理前端地址
在.env文件中添加前端地址:
FRONTEND_URL=http://127.0.0.1:5173
然后在server.js中引用:
app.use( cors({ origin: process.env.FRONTEND_URL, credentials: true, }) );
验证修复
重启后端服务,重新触发前端登录请求,CORS错误应该会消失,请求能正常返回数据并设置Cookie。
内容的提问来源于stack exchange,提问作者Steven
相关产品推荐
相关产品推荐

