我已经通过以下YouTube教程构建了一个全栈MERN(React + Node.js + MongoDB)应用程序-https://www.youtube.com/watch?v=FcxjCPeicvU
项目官方GitHub repo-https://github.com/codinginflow/MERN-course
在确保前端和后端应用程序在我的本地机器上正常工作后,我决定在线部署它们。为了部署前端和后端,我使用了Render。
我先把后端部署到Render,当我尝试把本地前端应用和部署的后端应用连接起来时,效果很好。我可以毫无问题地执行注册/登录等操作。
然后我也在Render上部署了我的前端应用程序(https://notes-app-xjr0.onrender.com)。当我点击上面的URL时,它毫无问题地出现在应用程序主页上。有趣的部分来了。
每当我登录/注册时,后端应该存储会话,因此应该在前端存储cookie以保持用户登录。紧接着,应用程序对notes API(https://notes-api-ttsn.onrender.com/api/notes)进行GET
调用,以获取与存储在cookie中的用户凭据相关联的笔记。但在我的例子中,在成功登录(POST
调用https://notes-api-ttsn.onrender.com/api/users/login)后,我再也看不到cookie了,因此notes API失败,出现了401 - User not authenticated
错误(下面的屏幕截图)。
(BACKEND)app.ts
中的Express-session和CORS配置
import express from "express"
import session from "express-session";
import cors from "cors";
const app = express();
app.use(
session({
secret: env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: true,
httpOnly: false,
sameSite: "none",
maxAge: 60 * 60 * 1000,
},
rolling: true,
store: MongoStore.create({
mongoUrl: env.MONGO_CONNECTION_STRING,
}),
})
);
app.use(cors({ origin: "*", credentials: true }));
(BACKEND)controllers/users.ts
中的登录控制器功能
import { RequestHandler } from "express";
import createHttpError from "http-errors";
import bcrypt from "bcrypt";
import UserModel from "../models/User";
interface LoginBody {
username?: string;
password?: string;
}
export const login: RequestHandler<unknown, unknown, LoginBody, unknown> = async (req, res, next) => {
const { username, password } = req.body;
try {
if (!username || !password) {
throw createHttpError(400, "Missing required fields");
}
const user = await UserModel.findOne({ username: username })
.select("+password +email")
.exec();
if (!user) {
throw createHttpError(401, "Invalid username or password!");
}
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
throw createHttpError(401, "Invalid username or password!");
}
req.session.userId = user._id;
res.status(201).json(user);
} catch (error) {
next(error);
}
};
(BACKEND)获取controllers/notes.ts
中的Notes控制器函数
import { RequestHandler } from "express";
import createHttpError from "http-errors";
import NoteModel from "../models/Note";
/* GET ALL NOTES FOR THE LOGGED IN USER */
export const getNotes: RequestHandler = async (req, res, next) => {
const authenticatedUserId = req.session.userId;
try {
const notes = await NoteModel.find({ userId: authenticatedUserId }).exec();
res.status(200).json(notes);
} catch (error) {
next(error);
}
};
(FRONTEND)network/fetchData.ts
中的取数功能
import { ConflictError, UnauthorizedError } from "../errors/http_errors";
export const fetchData = async (input: RequestInfo, init?: RequestInit) => {
const apiEndpoint = env.BACKEND_URL + input;
const response = await fetch(apiEndpoint, init);
if (response.ok) {
return response;
} else {
const errorBody = await response.json();
const { error: errorMessage } = errorBody;
if (response.status === 401) {
throw new UnauthorizedError(errorMessage);
} else if (response.status === 409) {
throw new ConflictError(errorMessage);
} else {
throw Error(
"Request failed with status : " +
response.status +
" message: " +
errorMessage
);
}
}
};
(FRONTEND)network/user-api.ts
中的登录功能
import { User } from "../models/user";
import { fetchData } from "./fetchData";
export type LoginCredentials = {
username: string;
password: string;
};
export const login = async (credentials: LoginCredentials): Promise<User> => {
const response = await fetchData("/api/users/login", {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify(credentials),
});
return response.json();
};
network/note-api.ts
中的(FRONTEND)便笺取数功能
import { Note } from "../models/note";
import { fetchData } from "./fetchData";
/* GET ALL NOTES FOR THE LOGGED IN USER */
export const fetchNotes = async (): Promise<Note[]> => {
const response = await fetchData("/api/notes", { method: "GET" });
return response.json();
};
1条答案
按热度按时间r8xiu3jd1#
在使用render部署时,我也遇到了同样的问题。
从这个resource。Render使用此子域“www.example.com”部署您的后端服务器onrender.com。它是一个列出的公共后缀,因此cookie不能在不同的子域之间共享-如果您使用自己的子域可能会解决这个问题。