使用 JWT 身份验证、MongoDB 和 Express.js 构建高级 CRUD API

为了在 Node.js 中构建更高级的 CRUD API,我们可以添加以下几个功能:

  • 数据库集成:我们不会使用内存数据库,而是集成真实的数据库(例如 MongoDB、PostgreSQL)。
  • 输入验证和错误处理:我们将使用 Joi 或 express-validator 等库进行输入验证,并改进错误处理。
  • 身份验证和授权:我们将添加 JWT 身份验证来保护 API。
  • API 文档:我们将集成 Swagger 来编写 API 文档。
  • 环境变量:我们将使用 dotenv 来处理敏感信息,如数据库凭据和 API 密钥。
  • 您可以按照以下方式实现此目的:

    1. 设置项目并安装依赖项

    创建新项目并安装必要的依赖项:

    mkdir advanced-crud-api
    cd advanced-crud-api
    npm init -y

    安装以下依赖项:

    npm install express mongoose dotenv joi jsonwebtoken bcryptjs body-parser
    npm install --save-dev nodemon
  • express:Web框架。
  • mongoose:MongoDB ORM。
  • dotenv:用于环境变量管理。
  • joi:用于输入验证。
  • jsonwebtoken:用于创建和验证 JWT 令牌。
  • bcryptjs:用于散列密码。
  • body-parser:解析请求主体的中间件。
  • 2.创建环境配置

    创建一个 `.env` 文件来存储敏感数据:

    touch .env

    在 `.env` 文件中添加以下配置:

    PORT=5000
    DB_URI=mongodb://localhost:27017/advanced-crud
    JWT_SECRET=your_jwt_secret_key

    3.创建模型和数据库配置

    创建一个 `models` 文件夹并在 `models/User.js` 中定义一个 `User` 模型:

    const mongoose = require('mongoose');
    
    const userSchema = new mongoose.Schema({
      name: {
        type: String,
        required: [true, 'Name is required'],
      },
      email: {
        type: String,
        required: [true, 'Email is required'],
        unique: true,
        match: [/^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/, 'Please provide a valid email'],
      },
      password: {
        type: String,
        required: [true, 'Password is required'],
        minlength: [6, 'Password should be at least 6 characters long'],
      },
    });
    
    module.exports = mongoose.model('User', userSchema);

    在这个模型中,我们包含了“姓名”、“电子邮件”和“密码”的验证规则。

    4.数据库连接设置

    创建一个 `config/db.js` 文件来设置数据库连接:

    const mongoose = require('mongoose');
    const dotenv = require('dotenv');
    
    dotenv.config();
    
    const connectDB = async () => {
      try {
        await mongoose.connect(process.env.DB_URI, {
          useNewUrlParser: true,
          useUnifiedTopology: true,
        });
        console.log('MongoDB connected');
      } catch (error) {
        console.error('Error connecting to MongoDB:', error.message);
        process.exit(1); // Exit process with failure
      }
    };
    
    module.exports = connectDB;

    5. JWT 身份验证辅助函数

    创建一个文件 `utils/auth.js` 来处理 JWT 生成和密码哈希处理:

    const jwt = require('jsonwebtoken');
    const bcrypt = require('bcryptjs');
    
    const generateToken = (id) => {
      return jwt.sign({ id }, process.env.JWT_SECRET, { expiresIn: '30d' });
    };
    
    const hashPassword = async (password) => {
      const salt = await bcrypt.genSalt(10);
      return await bcrypt.hash(password, salt);
    };
    
    const comparePasswords = async (password, hashedPassword) => {
      return await bcrypt.compare(password, hashedPassword);
    };
    
    module.exports = { generateToken, hashPassword, comparePasswords };

    6. 创建用于 CRUD 操作的控制器

    在 `controllers` 文件夹中,创建 `userController.js` 来处理 CRUD 操作。

    const User = require('../models/User');
    const Joi = require('joi');
    const { generateToken, hashPassword, comparePasswords } = require('../utils/auth');
    
    // Validation schema
    const userValidationSchema = Joi.object({
      name: Joi.string().min(3).required(),
      email: Joi.string().email().required(),
      password: Joi.string().min(6).required(),
    });
    
    // Register user
    exports.registerUser = async (req, res) => {
      try {
        const { name, email, password } = req.body;
    
        // Validate input data
        const { error } = userValidationSchema.validate(req.body);
        if (error) return res.status(400).json({ error: error.details[0].message });
    
        // Check if the user already exists
        const userExists = await User.findOne({ email });
        if (userExists) {
          return res.status(400).json({ message: 'User already exists' });
        }
    
        const hashedPassword = await hashPassword(password);
    
        const newUser = new User({ name, email, password: hashedPassword });
        await newUser.save();
    
        const token = generateToken(newUser._id);
    
        res.status(201).json({ user: newUser, token });
      } catch (err) {
        res.status(500).json({ message: 'Server error' });
      }
    };
    
    // Login user
    exports.loginUser = async (req, res) => {
      try {
        const { email, password } = req.body;
    
        // Validate input data
        const { error } = userValidationSchema.validate(req.body);
        if (error) return res.status(400).json({ error: error.details[0].message });
    
        const user = await User.findOne({ email });
        if (!user) return res.status(400).json({ message: 'Invalid email or password' });
    
        const isMatch = await comparePasswords(password, user.password);
        if (!isMatch) return res.status(400).json({ message: 'Invalid email or password' });
    
        const token = generateToken(user._id);
        res.json({ user, token });
      } catch (err) {
        res.status(500).json({ message: 'Server error' });
      }
    };
    
    // Get user details
    exports.getUserDetails = async (req, res) => {
      try {
        const user = await User.findById(req.user.id).select('-password');
        if (!user) return res.status(404).json({ message: 'User not found' });
    
        res.json(user);
      } catch (err) {
        res.status(500).json({ message: 'Server error' });
      }
    };
    
    // Update user details
    exports.updateUser = async (req, res) => {
      try {
        const { name, email } = req.body;
    
        const user = await User.findByIdAndUpdate(req.user.id, { name, email }, { new: true });
        if (!user) return res.status(404).json({ message: 'User not found' });
    
        res.json(user);
      } catch (err) {
        res.status(500).json({ message: 'Server error' });
      }
    };
    
    // Delete user
    exports.deleteUser = async (req, res) => {
      try {
        const user = await User.findByIdAndDelete(req.user.id);
        if (!user) return res.status(404).json({ message: 'User not found' });
    
        res.status(204).send();
      } catch (err) {
        res.status(500).json({ message: 'Server error' });
      }
    };

    7. 使用中间件保护路由

    创建一个中间件“middleware/auth.js”来保护需要身份验证的路由:

    const jwt = require('jsonwebtoken');
    
    const protect = (req, res, next) => {
      const token = req.header('Authorization')?.replace('Bearer ', '');
    
      if (!token) return res.status(401).json({ message: 'No token, authorization denied' });
    
      try {
        const decoded = jwt.verify(token, process.env.JWT_SECRET);
        req.user = decoded; // Attach user info to request
        next();
      } catch (err) {
        res.status(401).json({ message: 'Token is not valid' });
      }
    };
    
    module.exports = protect;

    8. 设置快速路线

    在 `routes/userRoutes.js` 中创建用户操作的路由:

    const express = require('express');
    const { registerUser, loginUser, getUserDetails, updateUser, deleteUser } = require('../controllers/userController');
    const protect = require('../middleware/auth');
    
    const router = express.Router();
    
    router.post('/register', registerUser);
    router.post('/login', loginUser);
    router.get('/me', protect, getUserDetails);
    router.put('/me', protect, updateUser);
    router.delete('/me', protect, deleteUser);
    
    module.exports = router;

    9. 设置主应用程序

    现在,在“index.js”中设置主服务器文件:

    const express = require('express');
    const dotenv = require('dotenv');
    const connectDB = require('./config/db');
    const userRoutes = require('./routes/userRoutes');
    
    dotenv.config();
    
    const app = express();
    
    // Connect to database
    connectDB();
    
    app.use(express.json()); // Parse incoming JSON requests
    app.use('/api/users', userRoutes); // Use the user routes
    
    const PORT = process.env.PORT || 5000;
    app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

    10.运行服务器

    最后,启动

    服务器:

    npm run dev

    您现在拥有一个功能齐全的高级 CRUD API,其中包含 JWT 身份验证、用户注册和错误处理。

    结论

    此高级设置涵盖:

  • MongoDB 集成。
  • 使用 Joi 进行输入验证。
  • 基于 JWT 的身份验证和授权。
  • 用户的注册、登录、更新、删除操作。
  • 使用 bcrypt 进行安全密码处理。