简体   繁体   English

MERN 应用程序 on.netlify 和 heroku (CORS) 的问题

[英]Problem with MERN app on netlify and heroku (CORS)

This my first question but I completely have noo idea what to do:/ I learn javascript technologies.这是我的第一个问题,但我完全不知道该怎么做:/我学习 javascript 技术。 I've written my MERN app where I handle login and register feature.我编写了我的 MERN 应用程序,用于处理登录和注册功能。 My backend is deployed on heroku but client side is deployed on.netlify.我的后端部署在 heroku 上,但客户端部署在 .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).在本地一切正常,但是当我在部署到 heroku 和 .netlify 后测试我的应用程序时,一切正常,直到我尝试向我的后端发送请求(例如在登录过程中)。 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. ".我的请求在大约 20-30 秒后等待处理,此后我收到包含此内容的通知 - “从来源“https://pokemon”访问“https://pokemontrainer-app.herokuapp.com/auth/signin”上的 XMLHttpRequest -trainer-mern-app.netlify.app' 已被 CORS 策略阻止:请求的资源上不存在 'Access-Control-Allow-Origin' header。 ”。 I've been looking for a solution.我一直在寻找解决方案。 Most often I saw infos about _redirects file for client build folder for.netlify.大多数情况下,我看到有关 .netlify 的客户端构建文件夹的 _redirects 文件的信息。 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?如果 _redirects 文件确实是解决方案,我可以简短地询问我应该如何准备吗?

this is my backend code:这是我的后端代码:

server.js file:服务器.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 file:登录&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}

client side code:客户端代码:

apiHandling.js file: 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); }); }

If it is needed, I can also send a github link with code.如果需要的话,我也可以发一个github的链接和代码。

Thank you in advance for your answers.预先感谢您的回答。

You can add a middleware into you express app.您可以将中间件添加到您的 Express 应用程序中。 I happened to write some cors config for my express api today.我今天碰巧为我的快递 api 写了一些 cors 配置。 For your reference (server side code):供您参考(服务器端代码):

const corsMiddleware = (req, res, next) => {
  res = applyCorsHeaders(res);
  if (req.method === 'OPTIONS') {
    res.status(200).end()
    return
  }
  next()
}

const applyCorsHeaders = res => {
  res.setHeader('Access-Control-Allow-Credentials', true);
  res.setHeader('Access-Control-Allow-Origin', '*')
  // or res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
  res.setHeader('Access-Control-Allow-Methods', 'GET,OPTIONS,PATCH,DELETE,POST,PUT')
  res.setHeader(
    'Access-Control-Allow-Headers',
    'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version'
  )
  return res;
}

Then use this middleware in your app:然后在你的应用中使用这个中间件:

app.use(corsMiddleware);

For more about CORS, here's one official intro: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS关于 CORS 的更多信息,这里有一个官方介绍: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

Thank you for all responses.感谢您的所有回复。 I decided to put my whole app on heroku. It works.我决定将我的整个应用程序放在 heroku 上。它有效。 CORS policy problem disappeared. CORS政策问题消失。 Problem was related to different domains (heroku for backend and.netlify for frontend).问题与不同的域有关(后端为 heroku,前端为 .netlify)。 I decided to use this solution because I have never tried to put frontend on heroku. It required a rearrangement of my project folder.我决定使用此解决方案,因为我从未尝试将前端放在 heroku 上。它需要重新安排我的项目文件夹。 Now my project structure is:现在我的项目结构是:

enter image description here在此处输入图像描述

  1. Go to Heroku.com Go 至 Heroku.com
  2. Click on your "Dashboard"点击你的“仪表板”
  3. Click "New"点击“新建”
  4. Click "Create New App"点击“创建新应用”
  • Give the app a name and click "Create App"为应用程序命名,然后单击“创建应用程序”
  1. in server.js file add:在 server.js 文件中添加:

 const path = require("path"); // app.use(express.static(path.join(__dirname, "client", "build"))); // app.get("*", (req, res) => { res.sendFile(path.join(__dirname, "client", "build", "index.html")); }); // remember - in heroku app set a port like this - const port = process.env.PORT || 5000;

  1. in client package.json file add "proxy":在客户端 package.json 文件中添加“代理”:

 "proxy": "http://localhost:8000" - or other url of your server

  1. set up enviroment variables in heroku app:在heroku app中设置环境变量:

Go to "Settings" Click "Reveal Config Vars" Add a new variable and click "Add". Go 到“Settings”点击“Reveal Config Vars”添加一个新变量并点击“Add”。

  1. add next script in package.json in root folder:在根文件夹的 package.json 中添加下一个脚本:

 "scripts": { "heroku-postbuild": "cd client && npm install --only=dev && npm install && npm run build" }

  1. in Procfile folder:在 Procfile 文件夹中:

 web: node server.js

  1. in root folder heroku git:remote -a app-name-from-heroku git init git add.在根文件夹 heroku git:remote -a app-name-from-heroku git init git 添加。 git commit -m "commit name" git push heroku main (or master) 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).我在 coursework.vschool.io(文章标题 - 将 MERN 应用程序部署到 Heroku(使用您的 Git Master Branch & MongoDB Atlas)中找到了这个明确的解决方案)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM