Mern 身份验证 API

Flying
2023-06-18 / 0 评论 / 103 阅读 / 正在检测是否收录...

在本文中,我们将从零开始创建一个 MERN 栈应用程序。此项目的视频可以在此处找到。前端将使用 React、Redux、React Router 和 React Bootstrap 构建单页面应用程序。后端将使用 Express、MongoDB 和 Mongoose 作为数据库,并使用 JWT(JSON Web Tokens)和 HTTP-only Cookie 来实现身份验证。我们还将使用 Redux 进行状态管理,并使用 Redux Toolkit 使事情变得更加简单。本文将分为两个部分。第一部分我们将创建后端,第二部分我们将创建前端。

final.png

本项目的主要焦点是设置身份验证。它将帮助你更好地理解在 API 中创建路由/控制器,以及在 React/Redux 中管理状态。

此项目的所有代码可以在此 GitHub 存储库中找到。

好了,让我们开始吧。

在我们开始之前,我只想提到,在 MERN 堆栈应用程序中,有很多实现身份验证的方法。有第三方服务,如 Auth0 和 Firebase,使其变得非常简单。还有许多不同的库和包可供使用,例如 Passport.js。我选择以这种方式进行,因为它非常简单,除了 jsonwebtokens 包之外,不需要任何第三方服务或库。这也是一个很好的学习经验。

这将按以下方式工作:我们将拥有一个用于身份验证和验证电子邮件和密码的路由。一旦验证通过,我们将创建一个 JSON Web Token,并将其作为 HTTP-only Cookie 发送回客户端。我们还将有一个用于注册用户的路由,一个用于获取用户配置文件的路由,以及一个用于更新用户配置文件的路由。我们还将有一个用于注销用户并清除 Cookie 的路由。

开始入门

和我做的大多数项目一样,我喜欢从头开始。我们将从后端开始,设置数据库并创建路由、模型和控制器。我们将创建一些身份验证中间件,最终的结果应该是一个完全功能的登录和注册功能,可以将 JWT 保存在 HTTP-only Cookie 中。完成后,我们可以转到前端并创建我们的 React 应用程序。

后端设置

让我们初始化一个新项目并安装我们的依赖项。我将创建一个名为 mern-auth 的新目录,然后运行 npm init -y 以使用默认设置初始化一个新项目。然后用 VS Code 打开它。

mkdir mern-auth
cd mern-auth
npm init -y
code .

所有服务器依赖项都将安装在此目录中。让我们安装 Express、Mongoose、bcryptjs、jsonwebtoken 和 cookie-parser。

npm i express dotenv mongoose bcryptjs jsonwebtoken cookie-parser

以下是这些软件包的简要说明:

  • Express——用于 Node.js 的 Web 框架
  • dotenv——从 .env 文件加载环境变量
  • mongoose—— MongoDB 对象建模工具
  • bcryptjs——用于散列和盐化用户密码的库
  • jsonwebtoken——用于生成 JWT 的库
  • cookie-parser——用于解析 Cookie 的中间件

我们将创建一个名为 backend 的目录,其中包含我们的服务器端代码。入口点将是 backend/server.js。现在让我们创建它。

mkdir backend
touch backend/server.js

backend/server.js 中,让我们设置一个非常基本的 Express 服务器并运行它。

const express = require('express');
const port = 5000;

const app = express();

app.get('/', (req, res) => res.send('API running'));

app.listen(port, () => console.log(`Server started on port ${port})`));

我们可以使用 node backend/server.js 运行服务器,但我们想要使用 nodemon,这样每次我们进行更改时都不必重启服务器。让我们将其安装为开发依赖项。

npm i -D nodemon

现在,让我们在 package.json 文件中添加一些 NPM 脚本。

"scripts": {
  "start": "node backend/server.js",
  "server": "nodemon backend/server.js"
},

现在我们可以运行 npm run server 来启动服务器。我们应该在 http://localhost:5000 的浏览器中看到消息“API running”。在生产中将使用 npm start。

安装 Postman

我们将使用 Postman 来测试我们的 API。如果你没有安装它,请访问 https://www.postman.com/downloads 并下载适合你的操作系统的版本。

你可以通过在 Postman 中向 http://localhost:5000 发出 GET 请求来测试 API,你应该看到消息“API running”。

ES 模块

在我的 Node.js 项目中,我喜欢使用 ES 模块而不是 CommonJS。这是一种更易读、更易于使用的新语法。这也是我们将在前端 React 应用程序中使用的语法。要使用它,我们只需在 package.json 文件中添加以下内容。

"type": "module",

现在,将 server.js 中的 Express 导入更改为以下内容:

import express from 'express';

你的服务器应该仍然可以正常工作。

环境变量

我想创建一个文件来存储环境变量,它们基本上是我们不希望硬编码到代码中的变量。比如数据库凭据、API 密钥等。我们将使用 dotenv 包来实现这一点。

让我们在项目的根目录中创建一个名为 .env 的文件,并添加以下内容:

NODE_ENV=development
PORT=5000

你还应将此文件添加到 .gitignore 文件中,以防止其被推送到 GitHub。在根目录中创建一个名为 .gitignore 的文件,并添加以下内容:

node_modules
.env

现在,在 server.js文件中,我们可以导入 dotenv 包并使用它来加载我们的环境变量。

import dotenv from 'dotenv';
dotenv.config();

现在更改端口以使用环境变量。

const port = process.env.PORT || 5000;

重新启动服务器,现在它应该使用.env 文件中的端口。你可以将数字更改为其他值进行测试。

用户路由和控制器
让我们设置我们的路由。我们将有以下路由:

  • POST /api/users——注册用户
  • POST /api/users/auth——验证用户并获取令牌
  • POST /api/users/logout ——注销用户并清除 Cookie
  • GET /api/users/profile—— 获取用户个人资料
  • PUT /api/users/profile——更新个人资料

让我们首先在 backend 目录中创建一个名为 routes 的目录,然后在其中创建一个名为 userRoutes.js 的文件。这将包含我们的所有用户路由。

虽然我们可以在这个文件中拥有所有的路由逻辑,但最好将其分离到一个控制器中。让我们在 backend 目录中创建一个名为 controllers 的目录,然后在其中创建一个名为 userController.js 的文件。

让我们首先创建一个单个的控制器函数,并将其连接到一个路由,只是为了让代码跑起来。在 userController.js 中添加以下代码。

// @desc    验证用户并获取令牌
// @route   POST /api/users/auth
// @access  Public
const authUser = (req, res) => {
  res.json({ message: 'Success' });
};

export { authUser };

我喜欢在每个控制器函数的顶部放置一条注释,描述它的功能、路由和访问级别。当然,这是可选的。

我们只是发送一个包含消息“Success”的 JSON 响应。现在,让我们将其连接到一个路由中。在 userRoutes.js 中添加以下代码。

import express from 'express';
const router = express.Router();
import { authUser } from '../controllers/userController.js';

router.route('/auth').post(authUser);

export default router;

我们正在从 userController.js 导入 authUser 函数,并将其连接到路由 POST /api/users/auth。现在,让我们将其引入 server.js 并将其连接到 /api/users 路由。

import userRoutes from './routes/userRoutes.js';

app.use('/api/users', userRoutes);

测试路由

打开 Postman 或任何 HTTP 客户端,向 http://localhost:5000/api/users/auth 发出 POST 请求。你应该会看到带有消息“Success”的 JSON 响应。

使用异步处理程序

我们将在控制器函数中使用 async/await。我们可以在每个函数中使用 try/catch 块,但那样会很冗长。相反,我们将创建一个函数,它将包装每个控制器函数并处理任何错误。为了保持代码简单,我们将安装 express-async-handler

npm i express-async-handler

现在,将其引入 userController.js,以便我们可以在函数中使用它。

import asyncHandler from 'express-async-handler';

现在,将 asyncHandler 添加到 authUser 函数中。

const authUser = asyncHandler(async (req, res) => {
  res.json({ message: 'Success' });
});

这将允许我们在函数中使用 async/await,并且如果出现错误,它将被传递给我们的自定义错误处理程序,我们将在此处创建它。

自定义错误处理程序

backend 中创建一个名为 middleware 的文件夹,然后在其中创建一个名为 errorMiddleware.js 的文件。这将包含我们的自定义错误处理程序。

const notFound = (req, res, next) => {
  const error = new Error(`Not Found - ${req.originalUrl}`);
  res.status(404);
  next(error);
};

const errorHandler = (err, req, res, next) => {
  let statusCode = res.statusCode === 200 ? 500 : res.statusCode;
  let message = err.message;

  // If Mongoose not found error, set to 404 and change message
  if (err.name === 'CastError' && err.kind === 'ObjectId') {
    statusCode = 404;
    message = 'Resource not found';
  }

  res.status(statusCode).json({
    message: message,
    stack: process.env.NODE_ENV === 'production' ? null : err.stack
  });
};

export { notFound, errorHandler };

我们创建了两个中间件函数。第一个将用作捕获所有不存在的路由。第二个将用作捕获路由中发生的任何错误。

因此,这将允许我们从任何控制器函数中抛出错误,并将其传递给我们的自定义错误处理程序中间件,然后响应适当的状态码和消息。

我们还检查了一种特定类型的错误,即 Mongoose 的 CastError。当将无效的 ID 传递给 Mongoose 查询时,将抛出此错误。我们进行了检查,如果出现该错误,我们将将状态码设置为 404,消息设置为“Resource not found”。

这段代码非常有用,我在大多数 Node 项目中都使用它。

现在,我们需要将这些函数引入 server.js 并使用它们。将以下内容添加到文件顶部。

import { notFound, errorHandler } from './middleware/errorMiddleware.js';

现在,在路由之后,将以下内容添加到文件底部。

app.use(notFound);
app.use(errorHandler);

添加其余路由

现在我们已经设置好错误处理程序,可以添加其余的控制器函数和路由。在userController.js中添加以下代码。

import asyncHandler from 'express-async-handler';

// @desc    验证用户并获取令牌
// @route   POST /api/users/auth
// @access  public
const authUser = asyncHandler(async (req, res) => {
  res.send('Auth User');
});

// @desc    注册用户
// @route   POST /api/users
// @access  public
const registerUser = asyncHandler(async (req, res) => {
  res.send('register user');
});

// @desc    注销用户并清除 Cookie
// @route   POST /api/users/logout
// @access  private
const logoutUser = asyncHandler(async (req, res) => {
  res.send('logout user');
});

// @desc    获取用户资料
// @route   GET /api/users/profile
// @access  private
const getUserProfile = asyncHandler(async (req, res) => {
  res.send('get profile');
});

// @desc    更新用户资料
// @route   PUT /api/users/profile
// @access  private
const updateUserProfile = asyncHandler(async (req, res) => {
  res.send('update profile');
});

目前我们只是发送一个字符串作为响应。稍后我们将添加实际的功能。

添加路由

现在所有的路由都应该工作,并且只会响应我们在控制器函数中添加的字符串。可以在 Postman 中进行测试。确保为每个路由选择正确的 HTTP 方法。

import express from 'express';
import {
  authUser,
  registerUser,
  logoutUser,
  getUserProfile,
  updateUserProfile
} from '../controllers/userController.js';

const router = express.Router();

router.post('/', registerUser);
router.post('/auth', authUser);
router.post('/logout', logoutUser);
router.route('/profile').get(getUserProfile).put(updateUserProfile);

export default router;

数据库设置

现在我们将设置数据库。我们将使用 MongoDB Atlas,这是一个云数据库服务。你可以在本地安装 MongoDB,但我更喜欢 Atlas,因为它易于设置和使用。

创建 MongoDB Atlas 账户

访问MongoDB Atlas并点击“Start Free”按钮。按照步骤创建账户并确认你的电子邮件地址。

创建账户后,登录并创建一个数据库/集群。可能会要求你先创建一个组织和项目,如果是这样,请先进行创建并按你的要求进行命名。

然后应该会要求你创建一个数据库,界面如下所示:

db-set-up.png

接下来,你将看到一个询问你希望选择哪个计划的屏幕。

db-type.png

选择免费的 M0 计划。保留 AWS 作为提供商。如果需要,可以更改集群名称和区域。然后点击“create”。

现在将提示你创建一个数据库用户。这是我们将用来连接数据库的用户。一定要点击“Create User”按钮。

create-user.png

向下滚动并点击“Add My IP Address”。这将允许你从计算机连接到数据库。然后点击“Finish & Close”。

现在你在云端拥有一个部署好的数据库。

创建数据库

我们有了一个集群,现在让我们创建实际的存储集合的数据库。点击“Browse Collections”,然后点击“Add My Own Data”。

输入一个数据库名称。我将使用“mernauth”。然后添加一个名为“users”的集合。

你可以从这里管理数据,但我不建议这样做。相反,你可以使用 MongoDB Compass,这是一个桌面应用程序。

连接字符串

现在我们需要获取数据库的连接字符串。点击“Connect”按钮,然后选择“Connect Your Application”。

复制连接字符串:

connection-string.png

现在,打开你的 .env`文件并添加以下行:

MONGO_URI=<你的连接字符串>

在连接字符串中需要更改几个内容。首先,用你为数据库用户创建的密码替换 <password>

在连接字符串中需要更改几个内容。首先,将 <password> 替换为你为数据库用户创建的密码。其次,你需要添加你的数据库名称。我使用的名称是“mernauth",因此我将将其添加到连接字符串的末尾。它应该如下所示:

mongodb+srv://brad123:brad123@tutorial-cluster.lh0tyop.mongodb.net/mernauth?retryWrites=true&w=majority

所以我的 .env 文件应该如下所示:

NODE_ENV=development
PORT=5000
MONGO_URI=mongodb+srv://brad123:brad123@tutorial-cluster.lh0tyop.mongodb.net/mernauth?retryWrites=true&w=majority

请记得将 .env 文件添加到你的 .gitignore 文件中,如果你还没有这样做的话。否则,你将与全球分享你的数据库密码。

连接到数据库

现在我们需要连接到数据库。我们将使用已经安装的 mongoose 来实现。创建一个名为“config”的新目录,并在其中添加一个名为 db.js 的文件。然后添加以下代码。

import mongoose from 'mongoose';

const connectDB = async () => {
  try {
    const conn = await mongoose.connect(process.env.MONGO_URI);
    console.log(`MongoDB Connected: ${conn.connection.host}`);
  } catch (error) {
    console.error(`Error: ${error.message}`);
    process.exit(1);
  }
};

export default connectDB;

这是使用 mongoose 连接到 MongoDB 数据库的一种常见方式。我们使用异步函数,以便可以使用 await 等待连接完成。如果发生错误,我们希望退出进程。否则,我们将记录已连接的主机。

现在,我们可以在 server.js 中导入此文件并调用 connectDB 函数。

import connectDB from './config/db.js';

const port = process.env.PORT || 5000; // Run under this line...

connectDB();

当你运行服务器时,你应该会在控制台上看到“MongoDB Connected”的消息。

用户模型

使用 Mongoose,我们需要为数据中的所有资源创建一个模型,对于我们来说,只有用户模型。在后端目录中创建一个名为 models 的新目录,并添加一个名为 userModel.js 的文件。然后添加以下代码。

import mongoose from 'mongoose';

const userSchema = mongoose.Schema({
  name: {
    type: String,
    required: true,
  },
  email: {
    type: String,
    required: true,
    unique: true,
  },
  password: {
    type: String,
    required: true,
  },
},
  {
    timestamps: true,
  }
);

const User = mongoose.model('User', userSchema);

export default User;

我们的用户模型非常简单。我们有一个名称、电子邮件和密码。我们还有用户创建和更新的时间戳。在电子商务课程中,我们有一个 isAdmin 字段,但在这里我们不需要。

注册路由

现在,我们已经连接到了数据库,并且有了一个用户模型,让我们开始编写我们的第一个路由/控制器函数,即注册用户的路由。打开 userController.js 并导入 User 模型。

import User from '../models/userModel.js';

然后在 registerUser 函数中添加以下内容。

const registerUser = asyncHandler(async (req, res) => {
  const { name, email, password } = req.body;

  const userExists = await User.findOne({ email });

  if (userExists) {
    res.status(400);
    throw new Error('User already exists');
  }

  const user = await User.create({
    name,
    email,
    password,
  });

  if (user) {
    res.status(201).json({
      _id: user._id,
      name: user.name,
      email: user.email,
    });
  } else {
    res.status(400);
    throw new Error('Invalid user data');
  }
});

我们从请求体中获取名称、电子邮件和密码。然后我们检查是否已经存在具有该电子邮件的用户。如果存在,我们抛出一个错误。如果不存在,我们创建用户并返回用户数据。

请求体解析中间件

为了能够从请求体中获取数据,我们需要在 server.js 中添加一些中间件。在 routes 之前的 server.js 中添加以下代码。

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

密码哈希

现在,如果我们使用 Postman 发送电子邮件和密码的请求,它将以明文形式存储在数据库中。我们不希望这样。我们希望在将其存储到数据库之前对密码进行哈希处理。我们将使用 bcryptjs 包进行哈希处理。

我们有几种方法可以实现这一点。我们可以在此处的控制器中实现,但我更喜欢在模型中实现。因此,打开 userModel.js 并添加以下代码。

import bcrypt from 'bcryptjs';

然后将以下代码添加到 userSchema

// Encrypt password using bcrypt
userSchema.pre('save', async function (next) {
  if (!this.isModified('password')) {
    next();
  }

  const salt = await bcrypt.genSalt(10);
  this.password = await bcrypt.hash(this.password, salt);
});

这将在用户保存到数据库之前运行。我们创建一个 salt,它是一串随机字符,然后我们使用 salt 对密码进行哈希处理。数字越大,密码的安全性越高,但哈希处理时间也越长。10 是一个不错的数字。

测试路由

打开 Postman,并向路由 http://localhost:5000/api/users 发送 POST 请求,并添加以下请求体数据:

{
  "name": "John Doe",
  "email": "john@email.com",
  "password": "123456"
}

你应该收到一个 201 状态码和用户数据。

test-route.png

现在,我们可以注册用户并将哈希密码存储在数据库中。你可以通过登录到 Atlas 或 Compass 来检查数据库。

JSON Web Tokens

现在,我们只是获取到用户的数据。我们需要一种方法来进行身份验证和检查用户,以便保护特定的路由。我们将使用 JSON Web Tokens 来实现。如果你一直在跟随操作步骤,那么你应该已经安装了 jsonwebtoken 包。如果没有安装,请现在安装它。

npm install jsonwebtoken

我们需要生成一个新的令牌并将其保存在 HTTP-only Cookie 中。我们可以在此文件中完成,但是我们必须在登录路由中执行相同的操作,所以我将这个功能放在一个实用程序文件中。

在后端目录中创建一个名为 utils 的文件夹,并添加一个名为 generateToken.js 的文件。然后添加以下代码。

import jwt from 'jsonwebtoken';

const generateToken = (res, userId) => {
  const token = jwt.sign({ userId }, process.env.JWT_SECRET, {
    expiresIn: '30d'
  });

  res.cookie('jwt', token, {
    httpOnly: true,
    secure: process.env.NODE_ENV !== 'development', // Use secure cookies in production
    sameSite: 'strict', // Prevent CSRF attacks
    maxAge: 30 * 24 * 60 * 60 * 1000 // 30 days
  });
};

export default generateToken;

我们使用 jwt.sign() 方法创建令牌。第一个参数是负载,即用户 ID。第二个参数是密钥,我们将在 .env 文件中添加。第三个参数是选项,即到期日期。我将其设置为 30 天,但你可能希望将其设置为较短的时间。然后,我们使用令牌设置 Cookie。

在你的 .env 文件中,添加以下内容:

JWT_SECRET=abc123

你的密钥可以是任意内容。

现在,让我们将实用程序文件引入控制器并使用它:

import generateToken from '../utils/generateToken.js';

然后在检查用户之后和发送响应之前立即调用它:

generateToken(res, user._id);

我们传入 resuser._id,它们将添加到令牌的负载中。

尝试注册一个新用户。如果你正在使用 Compass 或 Atlas,请删除用户 john 并注册相同的用户或创建一个新用户。

注册后,你应该在响应头中以及在 Postman 中看到 Cookie。

test-token.png

现在,该令牌应该随每个请求一起发送。因此,我们需要一个中间件函数来检查令牌并解码以获取用户 ID。

middleware 文件夹中创建一个名为 authMiddleware.js 的新文件,并添加以下代码:

import jwt from 'jsonwebtoken';
import asyncHandler from 'express-async-handler';
import User from '../models/userModel.js';

const protect = asyncHandler(async (req, res, next) => {
  let token;

  token = req.cookies.jwt;

  if (token) {
    try {
      const decoded = jwt.verify(token, process.env.JWT_SECRET);

      req.user = await User.findById(decoded.userId).select('-password');

      next();
    } catch (error) {
      console.error(error);
      res.status(401);
      throw new Error('Not authorized, token failed');
    }
  } else {
    res.status(401);
    throw new Error('Not authorized, no token');
  }
});

export { protect };

这里我们做的是从 Cookie 中获取令牌。然后我们检查是否存在令牌。如果存在,我们验证令牌并从负载中获取用户 ID。然后我们通过 ID 查找用户,并将用户附加到请求对象上。然后我们调用 next(),继续下一个中间件函数。如果没有令牌,我们抛出一个错误。

Cookie Parser Middleware

这很重要。我们使用 req.cookies.jwt 来访问 Cookie。为了做到这一点,我们需要添加 cookie-parser 中间件。确保已安装它。如果尚未安装,请立即安装。

npm install cookie-parser

然后在 server.js 文件中引入并使用它:

import cookieParser from 'cookie-parser';

app.use(cookieParser());

现在,我们可以将 protect 中间件引入到我们的路由文件中,并用它来保护我们想要保护的路由。让我们保护获取个人资料和更新个人资料的路由。

打开 userRoutes.js 并添加以下代码:

import { protect } from '../middleware/authMiddleware.js';

然后将 protect 作为第一个参数添加到 getProfileupdateProfile 路由中。

router
  .route('/profile')
  .get(protect, getUserProfile)
  .put(protect, updateUserProfile);

这样做的效果是,如果我们尝试在没有令牌的情况下访问这些路由,我们将收到一个错误。现在让我们测试一下。

现在我们有了 Cookie,这意味着我们可以在 Postman 中访问受保护的路由。请尝试向 http://localhost:5000/api/users/profile 发送一个 GET 请求。你应该收到字符串作为响应。

现在,进入浏览器,你没有 Cookie,在尝试访问相同的路由时,应该收到一个错误。

Auth 路由

现在我们可以保护路由并注册用户了。让我们来处理用于验证现有用户的路由/函数。

打开 userController.js 文件,进入 authUser 函数并添加以下代码:

const authUser = asyncHandler(async (req, res) => {
  const { email, password } = req.body;

  const user = await User.findOne({ email });

  if (user && (await user.matchPassword(password))) {
    generateToken(res, user._id);

    res.json({
      _id: user._id,
      name: user.name,
      email: user.email
    });
  } else {
    res.status(401);
    throw new Error('Invalid email or password');
  }
});

这与注册路由非常相似,只是我们不是将新用户添加到数据库中,而是检查用户是否存在以及密码是否匹配。如果是,则生成令牌并返回用户数据。

比较密码

请记住,数据库中的密码是经过哈希处理的,因此我们需要将哈希密码与用户在请求中发送的密码进行比较。我们使用了 user.matchPassword() 方法进行检查。我们需要在用户对象上创建该方法。我们可以在模型中完成。

打开 userModel.js 文件,并添加以下代码:

userSchema.methods.matchPassword = async function (enteredPassword) {
  return await bcrypt.compare(enteredPassword, this.password);
};

这是一个可以在用户对象上调用的方法。它接受用户在请求中发送的密码,并将其与数据库

中的哈希密码进行比较。它返回一个 Promise,所以我们需要使用 await

测试路由

现在,我们已经“登录”为 john 用户,因为我们设置了 Cookie/jwt。我们可以在 Postman 中删除 Cookie,方法是点击“cookies”选项卡并删除“jwt”Cookie。现在我们本质上已经注销。如果尝试访问个人资料路由,你将收到一个错误。

现在让我们尝试登录。向 http://localhost:5000/api/users/auth 发送一个 POST 请求,使用你创建的任何用户。首先使用错误的密码。你应该收到一个错误。现在使用正确的密码。对于我来说,密码是:

{
  "email": "john@email.com",
  "password": "123456"
}

你应该收到用户数据和响应头中的 Cookie。

现在我们可以注册和登录了。现在让我们添加登出功能。

登出路由

我们已经在 userController.js 文件中有一个名为 logoutUser 的函数。在该函数中添加以下代码:

const logoutUser = (req, res) => {
  res.cookie('jwt', '', {
    httpOnly: true,
    expires: new Date(0)
  });
  res.status(200).json({ message: 'Logged out successfully' });
};

这将清除 Cookie,并发送一条消息。尝试通过向 http://localhost:5000/api/users/logout 发送一个 POST 请求来测试它。你应该收到该消息,并且 Cookie 应该被清除。

获取用户个人资料路由

现在,我们已经设置了身份验证路由,让我们处理用户个人资料路由。

userController.js 文件的 getUserProfile 函数中添加以下代码:

const getUserProfile = asyncHandler(async (req, res) => {
  if (req.user) {
    res.json({
      _id: req.user._id,
      name: req.user.name,
      email: req.user.email
    });
  } else {
    res.status(404);
    throw new Error('User not found');
  }
});

由于该路由使用了 protect 中间件,我们可以在请求对象上访问用户对象。我们可以使用它从数据库中获取用户并发送回用户数据。

确保已登录,并向 http://localhost:5000/api/users/profile 发送一个 GET 请求。你应该收到用户数据。

更新用户个人资料路由

我们想要的最后一件事是使用户能够更新他们的个人资料。在 userController.js 文件的 updateUserProfile 函数中添加以下代码:

const updateUserProfile = asyncHandler(async (req, res) => {
  const user = await User.findById(req.user._id);

  if (user) {
    user.name = req.body.name || user.name;
    user.email = req.body.email || user.email;

    if (req.body.password) {
      user.password = req.body.password;
    }

    const updatedUser = await user.save();

    res.json({
      _id: updatedUser._id,
      name: updatedUser.name,
      email: updatedUser.email
    });
  } else {
    res.status(404);
    throw new Error('User not found');
  }
});

我们从数据库中获取用户并更新名称和电子邮件。如果用户发送了新密码,我们也会更新密码。然后,我们保存用户并发送更新后的用户数据。

确保已登录,并向 http://localhost:5000/api/users/profile 发送一个 PUT 请求,包含以下数据:

{
  "name": "John Update"
}

这应该更新你的用户的名称。

现在,你可以继续进行第二部分并实现前端部分。

原文:MERN 速成课程(第 1 部分)——后端API、中间件、数据库、JWT 和 HTTP-only Cookie

1

评论 (0)

取消