MERN应用程序在Netlify和Heroku(CORS)上的问题
这是我的第一个问题,但我完全有NOO知道该怎么做:/我学习JavaScript技术。我已经写了我的MERN应用程序,在其中处理登录并注册功能。我的后端部署在Heroku,但客户端部署在Netlify上。一切都在本地工作正常,但是当我在部署到Heroku并Netlify后测试我的应用程序时,一切都可以,直到我尝试将请求发送到后端(例如在登录过程中)。我的请求是在呼吸暂停的时间为20-30秒,此后我收到了此内容的通讯 - “ 访问'https://pokemontrainer-app.herokuapp.herokuapp.com/auth/signin'从origin中访问xmlhttprequest 'https://pokemon-trainer-mern-app.netlify.app'已被CORS策略阻止:在请求的资源上不存在'access-control-allow-origin'标题。 ”。我一直在寻找解决方案。大多数情况下,我看到有关_redirects文件的信息,用于netlify的客户端构建文件夹。不幸的是,关于此问题的文档非常简短且不清楚。也许你们中的一个问题也有类似的问题并成功解决了吗?如果_redirects文件确实是解决方案,我可以要求简短的ifnormation我应该如何准备吗?
这是我的后端代码:
server.js文件:
const express = require('express');
const cors = require('cors');
const mongoose = require('mongoose');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser')
const mainRoutes = require('./routes/main.js');
const signinSignupRoutes = require('./routes/signInSignUp.js');
const userTrainersRoutes = require('./routes/userTrainers.js');
require('dotenv').config({ path: './.env' });
const app = express();
const port = process.env.PORT || 8000;
console.log(process.env.FRONTEND_URI);
//------Express-------
app.use(bodyParser.json({limit: '500mb'}));
app.use(bodyParser.urlencoded({limit: '500mb', extended: true}));
app.use(cookieParser());
app.use(express.json());
app.use(express.urlencoded());
app.use(cors(
{
credentials: true,
origin: 'https://pokemon-trainer-mern-app.netlify.app'
})
);
app.use('/', mainRoutes);
app.use('/auth', signinSignupRoutes);
app.use('/loggedUser', userTrainersRoutes);
//------Mongoose-------
const main = async() => {
try {
await mongoose.connect(`mongodb+srv://${process.env.USERS_USERNAME}:${process.env.USERS_API_KEY}@pokemon-app.2s1cy.mongodb.net/myFirstDatabase?retryWrites=true&w=majority`);
console.log('Database connection works!')
}catch(err) {
console.log(err.message);
}
}
main()
.then(()=> app.listen(port, () => {
console.log(`Server works on port ${port}`);
}))
.catch(err => console.log(err.message));
signin& up.js文件:
const bcrypt = require('bcryptjs');
const Joi = require('joi');
const jwt = require('jsonwebtoken');
const {User, validation} = require('../models/user.js');
const getUsers = async (req, res) => {
try {
const users = await User.find();
res.status(200).json(users);
} catch(err) {
res.status(404).json(err.message);
}
}
const signUp = async(req, res) => {
try{
const {error} = validation(req.body);
error && res.status(400).send({message: error.details[0].message});
const user = await User.findOne({email: req.body.email});
if(user) {
res.status(409).send({message: 'User with this email already exists.'})
} else {
if(req.body.userName === "") {
res.status(400).send({message: `Username field is empty`});
} else if(req.body.password !== req.body.confirmPassword || req.body.password === "") {
res.status(400).send({message: `Passwords aren't the same or password field is empty`});
} else {
const hashedPassword = await bcrypt.hash(req.body.password, 12);
await User.create({email: req.body.email, userName: req.body.userName, password: hashedPassword});
res.status(201).send({message: 'User registered succesfully!'});
}
}
} catch(err) {
res.status(500).send({message: 'Internal server error :('});
}
}
const signIn = async(req, res) => {
res.setHeader('Access-Control-Allow-Origin', 'https://pokemon-trainer-mern-app.netlify.app');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
res.setHeader('Access-Control-Allow-Credentials', true);
try{
const {error} = signInValidation(req.body);
error && res.status(400).send({message: error.details[0].message});
const user = await User.findOne({email: req.body.email});
!user && res.status(401).send({message: 'User with this email adress is not registered :('});
const validatedPassword = await bcrypt.compare(req.body.password, user.password);
!validatedPassword && res.status(401).send({message: 'Incorrect password :('});
const token = await user.generateAuthToken(user._id, user.email);
res.cookie('token', token, {
maxAge: 7 * 24 * 60 * 60 * 1000,
httpOnly: process.env.NODE_ENV === 'production' ? true : false,
secure: false,
}).status(200).send({message: 'Log in succesfully', userData: {userId:user._id, userName: user.userName, email: user.email, trainers: user.trainers, logged: true}});
} catch(err) {
console.log(err.message);
}
}
const signInViaGoogle = async(req, res) => {
try{
const user = await User.findOne({email: req.body.email});
!user && res.status(401).send({message:'You have to register your account with this email in this app'});
const token = user.generateAuthToken(user._id, user.email);
res.cookie('token', token, {
maxAge: 7 * 24 * 60 * 60 * 1000,
httpOnly: process.env.NODE_ENV === `${production}` ? true : false,
secure: false,
}).status(200).send({message: 'Log in succesfully', userData: {userId:user._id, userName: user.userName,email: user.email, trainers: user.trainers, logged: true}});
} catch(err) {
res.status(500).send({message: 'Internal server error :('});
}
}
const logout = async(req, res) => {
try {
const {token} = req.cookies;
token && res.clearCookie('token').send({message: 'Cookie cleared'});
} catch(err) {
res.status(500).send({message: 'Internal server error :('});
}
}
const newSession = async(req, res) => {
const {token} = req.cookies;
!token ? res.status(200).send({cookie: false, logged: false}) : res.status(200).send({cookie: true, logged: true});
}
// validation for signIn
const signInValidation = (data) => {
const JoiSchema = Joi.object({
email: Joi.string().required().label('E-mail'),
password: Joi.string().required().label('Password'),
});
return JoiSchema.validate(data);
}
module.exports = {getUsers, signUp, signIn, signInViaGoogle, logout, newSession}
客户端代码:
apihandling.js文件:
import axios from 'axios';
import { loginNativeUser, updateUserData, newSession } from '../actions/userActions.js'
const url = 'https://pokemontrainer-app.herokuapp.com';
const instance = axios.create({
baseUrl: url,
withCredentials: true,
credentials: 'include',
})
export const newSess = async (dispatch) => {
await instance.get(`${url}/auth/newSession`)
.then(res => {
dispatch(newSession(res.data));
})
.catch(err => console.log(err.message));
}
export const signInByGoogle = async (userData, setError, history, dispatch) => {
await instance.post(`${url}/auth/signin/google`, {
email: userData.email,
})
.then(res => {
setError(null);
dispatch(loginNativeUser(res.data.userData));
history.push('/');
})
.catch(err => {
setError(err.response.data.message);
history.push('/auth/signin');
alert(err.response.data.message);
})
}
export const signIn = async (formData, setError, history, dispatch) => {
await instance.post(`${url}/auth/signin`, {
password: formData.password,
email: formData.email,
})
.then(res => {
setError(null);
dispatch(loginNativeUser(res.data.userData));
history.push('/');
})
.catch(err => {
setError(err.response.data.message);
history.push('/auth/signin');
alert(err.response.data.message);
});
}
export const signUp = async (formData, setError, history) => {
await instance.post(`${url}/auth/signup`, {
userName: formData.userName,
password: formData.password,
confirmPassword: formData.confirmPassword,
email: formData.email,
})
.then(res => {
setError(null);
history.push('/');
alert('Registered succesfully')
})
.catch(err => {
setError(err.response.data.message);
history.push('/auth/signup');
alert(err.response.data.message);
});
}
export const cookieClear = async () => {
await instance.get(`${url}/auth/deleteCookie`)
.then(res => {
console.log('Cookie cleared');
})
.catch(err => {
console.log(err.response.data.message);
});
}
export const addTrainer = async (userId, trainer) => {
await instance.patch(`${url}/loggedUser/${userId}/addTrainer`, {
userId: userId,
trainer: trainer
})
.then(res => {
alert('Trainer added');
})
.catch(err => {
alert(err.response.data.message);
});
}
export const removeTrainer = async (userId, trainerId) => {
await instance.patch(`${url}/loggedUser/${userId}/${trainerId}/removeTrainer`, {
userId: userId,
trainerId: trainerId
})
.then(res => {
alert(res.data.message);
})
.catch(err =>{
alert(err.response.data.message);
})
}
export const addPokemon = async(userId, trainerId, pokemon) => {
await instance.patch(`${url}/loggedUser/${userId}/${trainerId}/${pokemon}/addPokemon`, {
userId: userId,
trainerId: trainerId,
pokemon: pokemon
})
.then(res => {
alert('Pokemon caught');
})
.catch((err) => {
alert(err.response.data.message);
})
}
export const updateData = async (userId, dispatch) => {
await instance.post(`${url}/loggedUser/${userId}/updateData`, {
userId: userId,
})
.then(res => {
dispatch(updateUserData(res.data.userData));
})
.catch(err => {
console.log(err.response.data.message);
});
}
如果需要,我还可以发送带有代码的github链接。
预先感谢您的回答。
This my first question but I completely have noo idea what to do :/ I learn javascript technologies. I've written my MERN app where I handle login and register feature. My backend is deployed on heroku but client side is deployed on netlify. Everything is working fine locally, but when I test my app after deployment to heroku and netlify, everything is ok till I try send a request to my backend (for example during login process). My request is pending aproximately 20-30 sec and after this time I receive annoucement with this content - "Access to XMLHttpRequest at 'https://pokemontrainer-app.herokuapp.com/auth/signin' from origin 'https://pokemon-trainer-mern-app.netlify.app' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.". I've been looking for a solution. Most often I saw infos about _redirects file for client build folder for netlify. Unfortunately documentation is very short and unclear when it comes to this issue. Maybe one of you had a similar problem and resolved it with success? If _redirects file is really solution, can I ask for short ifnormation how I should prepare it?
this is my backend code:
server.js file:
const express = require('express');
const cors = require('cors');
const mongoose = require('mongoose');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser')
const mainRoutes = require('./routes/main.js');
const signinSignupRoutes = require('./routes/signInSignUp.js');
const userTrainersRoutes = require('./routes/userTrainers.js');
require('dotenv').config({ path: './.env' });
const app = express();
const port = process.env.PORT || 8000;
console.log(process.env.FRONTEND_URI);
//------Express-------
app.use(bodyParser.json({limit: '500mb'}));
app.use(bodyParser.urlencoded({limit: '500mb', extended: true}));
app.use(cookieParser());
app.use(express.json());
app.use(express.urlencoded());
app.use(cors(
{
credentials: true,
origin: 'https://pokemon-trainer-mern-app.netlify.app'
})
);
app.use('/', mainRoutes);
app.use('/auth', signinSignupRoutes);
app.use('/loggedUser', userTrainersRoutes);
//------Mongoose-------
const main = async() => {
try {
await mongoose.connect(`mongodb+srv://${process.env.USERS_USERNAME}:${process.env.USERS_API_KEY}@pokemon-app.2s1cy.mongodb.net/myFirstDatabase?retryWrites=true&w=majority`);
console.log('Database connection works!')
}catch(err) {
console.log(err.message);
}
}
main()
.then(()=> app.listen(port, () => {
console.log(`Server works on port ${port}`);
}))
.catch(err => console.log(err.message));
signIn&Up.js file:
const bcrypt = require('bcryptjs');
const Joi = require('joi');
const jwt = require('jsonwebtoken');
const {User, validation} = require('../models/user.js');
const getUsers = async (req, res) => {
try {
const users = await User.find();
res.status(200).json(users);
} catch(err) {
res.status(404).json(err.message);
}
}
const signUp = async(req, res) => {
try{
const {error} = validation(req.body);
error && res.status(400).send({message: error.details[0].message});
const user = await User.findOne({email: req.body.email});
if(user) {
res.status(409).send({message: 'User with this email already exists.'})
} else {
if(req.body.userName === "") {
res.status(400).send({message: `Username field is empty`});
} else if(req.body.password !== req.body.confirmPassword || req.body.password === "") {
res.status(400).send({message: `Passwords aren't the same or password field is empty`});
} else {
const hashedPassword = await bcrypt.hash(req.body.password, 12);
await User.create({email: req.body.email, userName: req.body.userName, password: hashedPassword});
res.status(201).send({message: 'User registered succesfully!'});
}
}
} catch(err) {
res.status(500).send({message: 'Internal server error :('});
}
}
const signIn = async(req, res) => {
res.setHeader('Access-Control-Allow-Origin', 'https://pokemon-trainer-mern-app.netlify.app');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
res.setHeader('Access-Control-Allow-Credentials', true);
try{
const {error} = signInValidation(req.body);
error && res.status(400).send({message: error.details[0].message});
const user = await User.findOne({email: req.body.email});
!user && res.status(401).send({message: 'User with this email adress is not registered :('});
const validatedPassword = await bcrypt.compare(req.body.password, user.password);
!validatedPassword && res.status(401).send({message: 'Incorrect password :('});
const token = await user.generateAuthToken(user._id, user.email);
res.cookie('token', token, {
maxAge: 7 * 24 * 60 * 60 * 1000,
httpOnly: process.env.NODE_ENV === 'production' ? true : false,
secure: false,
}).status(200).send({message: 'Log in succesfully', userData: {userId:user._id, userName: user.userName, email: user.email, trainers: user.trainers, logged: true}});
} catch(err) {
console.log(err.message);
}
}
const signInViaGoogle = async(req, res) => {
try{
const user = await User.findOne({email: req.body.email});
!user && res.status(401).send({message:'You have to register your account with this email in this app'});
const token = user.generateAuthToken(user._id, user.email);
res.cookie('token', token, {
maxAge: 7 * 24 * 60 * 60 * 1000,
httpOnly: process.env.NODE_ENV === `${production}` ? true : false,
secure: false,
}).status(200).send({message: 'Log in succesfully', userData: {userId:user._id, userName: user.userName,email: user.email, trainers: user.trainers, logged: true}});
} catch(err) {
res.status(500).send({message: 'Internal server error :('});
}
}
const logout = async(req, res) => {
try {
const {token} = req.cookies;
token && res.clearCookie('token').send({message: 'Cookie cleared'});
} catch(err) {
res.status(500).send({message: 'Internal server error :('});
}
}
const newSession = async(req, res) => {
const {token} = req.cookies;
!token ? res.status(200).send({cookie: false, logged: false}) : res.status(200).send({cookie: true, logged: true});
}
// validation for signIn
const signInValidation = (data) => {
const JoiSchema = Joi.object({
email: Joi.string().required().label('E-mail'),
password: Joi.string().required().label('Password'),
});
return JoiSchema.validate(data);
}
module.exports = {getUsers, signUp, signIn, signInViaGoogle, logout, newSession}
client side code:
apiHandling.js file:
import axios from 'axios';
import { loginNativeUser, updateUserData, newSession } from '../actions/userActions.js'
const url = 'https://pokemontrainer-app.herokuapp.com';
const instance = axios.create({
baseUrl: url,
withCredentials: true,
credentials: 'include',
})
export const newSess = async (dispatch) => {
await instance.get(`${url}/auth/newSession`)
.then(res => {
dispatch(newSession(res.data));
})
.catch(err => console.log(err.message));
}
export const signInByGoogle = async (userData, setError, history, dispatch) => {
await instance.post(`${url}/auth/signin/google`, {
email: userData.email,
})
.then(res => {
setError(null);
dispatch(loginNativeUser(res.data.userData));
history.push('/');
})
.catch(err => {
setError(err.response.data.message);
history.push('/auth/signin');
alert(err.response.data.message);
})
}
export const signIn = async (formData, setError, history, dispatch) => {
await instance.post(`${url}/auth/signin`, {
password: formData.password,
email: formData.email,
})
.then(res => {
setError(null);
dispatch(loginNativeUser(res.data.userData));
history.push('/');
})
.catch(err => {
setError(err.response.data.message);
history.push('/auth/signin');
alert(err.response.data.message);
});
}
export const signUp = async (formData, setError, history) => {
await instance.post(`${url}/auth/signup`, {
userName: formData.userName,
password: formData.password,
confirmPassword: formData.confirmPassword,
email: formData.email,
})
.then(res => {
setError(null);
history.push('/');
alert('Registered succesfully')
})
.catch(err => {
setError(err.response.data.message);
history.push('/auth/signup');
alert(err.response.data.message);
});
}
export const cookieClear = async () => {
await instance.get(`${url}/auth/deleteCookie`)
.then(res => {
console.log('Cookie cleared');
})
.catch(err => {
console.log(err.response.data.message);
});
}
export const addTrainer = async (userId, trainer) => {
await instance.patch(`${url}/loggedUser/${userId}/addTrainer`, {
userId: userId,
trainer: trainer
})
.then(res => {
alert('Trainer added');
})
.catch(err => {
alert(err.response.data.message);
});
}
export const removeTrainer = async (userId, trainerId) => {
await instance.patch(`${url}/loggedUser/${userId}/${trainerId}/removeTrainer`, {
userId: userId,
trainerId: trainerId
})
.then(res => {
alert(res.data.message);
})
.catch(err =>{
alert(err.response.data.message);
})
}
export const addPokemon = async(userId, trainerId, pokemon) => {
await instance.patch(`${url}/loggedUser/${userId}/${trainerId}/${pokemon}/addPokemon`, {
userId: userId,
trainerId: trainerId,
pokemon: pokemon
})
.then(res => {
alert('Pokemon caught');
})
.catch((err) => {
alert(err.response.data.message);
})
}
export const updateData = async (userId, dispatch) => {
await instance.post(`${url}/loggedUser/${userId}/updateData`, {
userId: userId,
})
.then(res => {
dispatch(updateUserData(res.data.userData));
})
.catch(err => {
console.log(err.response.data.message);
});
}
If it is needed, I can also send a github link with code.
Thank you in advance for your answers.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您可以将中间件添加到Express应用中。
我今天碰巧为我的Express API编写了一些CORS配置。
对于您的参考(服务器端代码):
然后在应用程序中使用此中间件:
有关CORS的更多信息,这是一个官方介绍: https://developer.mozilla.org/en-us/docs/web/http/cors
You can add a middleware into you express app.
I happened to write some cors config for my express api today.
For your reference (server side code):
Then use this middleware in your app:
For more about CORS, here's one official intro: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
感谢您的所有回应。我决定将整个应用程序放在Heroku上。有用。 CORS政策问题消失了。问题与不同的领域有关(Heroku用于后端和Frontend的Netlify)。我决定使用此解决方案,因为我从未尝试过将正端放在Heroku上。它需要重新安排我的项目文件夹。现在我的项目结构是:
在此处输入图像说明
转到“设置”
单击“揭示config vars”
添加一个新变量,然后单击“添加”。
Heroku git:远程 - 一个app-name-from-heroku
git init
git添加。
git commit -m“提交名称”
您应用程序应该有效的Git推动Heroku Main(或主)
。我在Coursework.vschool.io(文章标题 - 将MERN应用程序部署到Heroku(使用您的git Master Branch& mongodb atlas)中找到了这个清晰的解决方案。
Thank you for all responses. I decided to put my whole app on heroku. It works. CORS policy problem disappeared. Problem was related to different domains (heroku for backend and netlify for frontend). I decided to use this solution because I have never tried to put frontend on heroku. It required a rearrangement of my project folder. Now my project structure is:
enter image description here
Go to "Settings"
Click "Reveal Config Vars"
Add a new variable and click "Add".
heroku git:remote -a app-name-from-heroku
git init
git add .
git commit -m "commit name"
git push heroku main (or master)
You app should works. I found this clear solution in coursework.vschool.io (article title - Deploying MERN App to Heroku (using your Git Master Branch & MongoDB Atlas).