使用 JWT 身份验证、MongoDB 和 Express.js 构建高级 CRUD API
为了在 Node.js 中构建更高级的 CRUD 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
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 身份验证、用户注册和错误处理。
结论
此高级设置涵盖: