前言
默认大家已经看过前面的文章,并成功搭建项目。这章主要写项目的环境搭建,比如基础结构文件,配置数据库,注册登录功能。注册登录功能放这章,是因为要用他来写测试方法。文件你们对照着创建,代码文件我等会来加注释,写到具体CURD功能之前我会停一下,并且把具体测试的代码,并写下来。
设置项目基础结构和基础配置
首先,创建必要的项目结构,类似.gitignore的文件请大家按照第一章结构自己对比创建
mkdir config controllers middleware models routes
然后创建.env文件
touch .env
在文件中加入
PORT=3000
MONGODB_URI=mongodb://localhost:27017/blog
JWT_SECRET=your_jwt_secret_key
创建数据库配置文件config/db.js
// 导入 mongoose 模块
const mongoose = require('mongoose');
/**
* 连接 MongoDB 数据库
* @returns {Promise} 返回数据库连接结果
*/
const connectDB = async () => {
try {
// 使用环境变量中的 MONGODB_URI 连接数据库
const conn = await mongoose.connect(process.env.MONGODB_URI);
// 连接成功,输出连接主机信息
console.log(`MongoDB Connected: ${conn.connection.host}`);
} catch (error) {
// 连接失败,输出错误信息
console.error(`Error: ${error.message}`);
// 终止进程
process.exit(1);
}
};
// 导出数据库连接函数
module.exports = connectDB;
累了,点根烟。等会回来写
回来了,继续
修改app.js文件
// 导入必要的模块
// dotenv: 用于从 .env 文件加载环境变量
require('dotenv').config();
// express: Web 应用框架
const express = require('express');
// cors: 处理跨域资源共享
const cors = require('cors');
// morgan: HTTP 请求日志记录
const morgan = require('morgan');
// cookie-parser: 解析 Cookie 头
const cookieParser = require('cookie-parser');
// 导入数据库连接配置
const connectDB = require('./config/db');
// 创建 Express 应用实例
const app = express();
// 初始化数据库连接
connectDB();
// 配置中间件
// 启用 CORS
app.use(cors());
// 解析 JSON 请求体
app.use(express.json());
// 解析 URL 编码的请求体
app.use(express.urlencoded({ extended: false }));
// 解析 Cookie
app.use(cookieParser());
// 启用开发环境下的请求日志
app.use(morgan('dev'));
// 定义根路由
app.get('/', (req, res) => {
res.json({ message: 'Welcome to Blog API' });
});
// 全局错误处理中间件
// 捕获并处理所有未处理的错误
app.use((err, req, res, next) => {
// 在控制台输出错误堆栈
console.error(err.stack);
// 返回 500 错误响应
res.status(500).json({ message: 'Something went wrong!' });
});
// 导出 Express 应用实例
module.exports = app;
验证
到这一步的时候验证下之前代码是否正确
启动服务
npm run dev
浏览器访问
http://localhost:3000/
返回下列信息就可以进行下一步,否则请检查下代码
{
"message": "Welcome to Blog API"
}
在根目录创建数据库连接测试文件test-db.js
require('dotenv').config();
const mongoose = require('mongoose');
const testConnection = async () => {
try {
// 连接数据库
await mongoose.connect(process.env.MONGODB_URI);
console.log('✅ MongoDB 连接成功!');
// 测试数据库操作
const collections = await mongoose.connection.db.listCollections().toArray();
console.log('📚 数据库中的集合:', collections.map(c => c.name));
// 测试完成后断开连接
await mongoose.connection.close();
console.log('✅ 数据库连接已关闭');
} catch (error) {
console.error('❌ 连接错误:', error.message);
}
};
testConnection();
运行测试
node test-db.js
返回下列信息就可以进行下一步,否则请检查下代码
✅ MongoDB 连接成功!
📚 数据库中的集合: []
✅ 数据库连接已关闭
OK,让我们继续
创建用户模型文件models/User.js
// mongoose 是一个用于连接和操作 MongoDB 数据库的库
const mongoose = require('mongoose');
// bcrypt 是一个用于密码加密的库,用于将密码加密为哈希值,以提高安全性
const bcrypt = require('bcryptjs');
// 定义用户模型架构
const userSchema = new mongoose.Schema({
username: {
type: String, // 字符串类型
required: true, // 必填字段
unique: true, // 唯一值
trim: true // 去除字符串两端的空白字符
},
email: {
type: String,
required: true,
unique: true,
trim: true,
lowercase: true // 小写化
},
password: {
type: String,
required: true
},
role: {
type: String,
enum: ['user', 'admin'], // 枚举类型
default: 'user' // 默认值
}
}, {
timestamps: true // 自动添加创建和更新时间戳
});
// 密码加密中间件 - 在保存用户前自动加密密码
userSchema.pre('save', async function(next) {
// 如果密码没有被修改,则跳过加密
if (!this.isModified('password')) return next();
// 生成盐值
const salt = await bcrypt.genSalt(10);
// 加密密码
this.password = await bcrypt.hash(this.password, salt);
});
// 验证密码方法 - 用于登录时验证密码
userSchema.methods.matchPassword = async function(enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password);
};
// 创建并导出用户模型
const User = mongoose.model('User', userSchema);
module.exports = User;
创建认证中间件middleware/auth.js
// 导入必要的模块
const jwt = require('jsonwebtoken');
const User = require('../models/User');
// 身份验证中间件
const protect = async (req, res, next) => {
try {
let token;
// 从请求头中获取 Bearer token
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
token = req.headers.authorization.split(' ')[1];
}
// 如果没有token,返回未授权错误
if (!token) {
return res.status(401).json({ message: 'Not authorized, no token' });
}
// 验证token并获取用户信息
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = await User.findById(decoded.id).select('-password');
next();
} catch (error) {
// token验证失败,返回未授权错误
res.status(401).json({ message: 'Not authorized, token failed' });
}
};
// 导出中间件
module.exports = { protect };
实现用户认证系统 登录/注册
创建认证控制器controllers/authController.js
const User = require('../models/User');
const jwt = require('jsonwebtoken');
// 生成 JWT Token
const generateToken = (id) => {
return jwt.sign({ id }, process.env.JWT_SECRET, {
expiresIn: '30d'
});
};
// 注册新用户
const register = async (req, res) => {
try {
const { username, email, password } = req.body;
// 检查用户是否已存在
const userExists = await User.findOne({ $or: [{ email }, { username }] });
if (userExists) {
return res.status(400).json({ message: '用户已存在' });
}
// 创建新用户
const user = await User.create({
username,
email,
password
});
// 生成 token
const token = generateToken(user._id);
res.status(201).json({
_id: user._id,
username: user.username,
email: user.email,
token
});
} catch (error) {
res.status(500).json({ message: error.message });
}
};
// 用户登录
const login = async (req, res) => {
try {
const { email, password } = req.body;
// 查找用户
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ message: '邮箱或密码错误' });
}
// 验证密码
const isMatch = await user.matchPassword(password);
if (!isMatch) {
return res.status(401).json({ message: '邮箱或密码错误' });
}
// 生成 token
const token = generateToken(user._id);
res.json({
_id: user._id,
username: user.username,
email: user.email,
token
});
} catch (error) {
res.status(500).json({ message: error.message });
}
};
// 获取当前用户信息
const getMe = async (req, res) => {
try {
const user = await User.findById(req.user._id).select('-password');
res.json(user);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
module.exports = {
register,
login,
getMe
};
创建认证路由routes/auth.js
const express = require('express');
const router = express.Router();
const { register, login, getMe } = require('../controllers/authController');
const { protect } = require('../middleware/auth');
// 注册路由
router.post('/register', register);
// 登录路由
router.post('/login', login);
// 获取当前用户信息
router.get('/me', protect, getMe);
module.exports = router;
在app.js中添加认证路由
// ... 其他导入
const authRoutes = require('./routes/auth');
// ... 中间件配置
// 路由
app.use('/api/auth', authRoutes);
// ... 错误处理
创建请求验证中间件middleware/validateRequest.js
// 请求验证中间件
// 接收一个验证模式(schema)作为参数,返回一个中间件函数
const validateRequest = (schema) => {
return (req, res, next) => {
try {
// 使用 schema 验证请求体
const { error } = schema.validate(req.body);
// 如果验证失败,返回错误信息
if (error) {
return res.status(400).json({ message: error.details[0].message });
}
// 验证通过,继续下一个中间件
next();
} catch (error) {
// 捕获其他错误并返回 500 错误
res.status(500).json({ message: error.message });
}
};
};
module.exports = validateRequest;
创建验证模式middleware/validationSchemas.js
// 导入 Joi 验证库
const Joi = require('joi');
// 注册表单验证规则
const registerSchema = Joi.object({
username: Joi.string().min(3).max(30).required(), // 用户名长度3-30字符
email: Joi.string().email().required(), // 必须是有效的邮箱格式
password: Joi.string().min(6).required() // 密码最少6个字符
});
// 登录表单验证规则
const loginSchema = Joi.object({
email: Joi.string().email().required(), // 必须是有效的邮箱格式
password: Joi.string().required() // 密码不能为空
});
// 导出验证规则
module.exports = {
registerSchema,
loginSchema
};
安装新的依赖
npm install joi
更新新的路由 routes/auth.js
// 导入必要的模块
const express = require('express');
const router = express.Router();
// 导入认证控制器函数
const { register, login, getMe } = require('../controllers/authController');
// 导入认证中间件
const { protect } = require('../middleware/auth');
// 导入请求验证中间件
const validateRequest = require('../middleware/validateRequest');
// 导入验证模式
const { registerSchema, loginSchema } = require('../middleware/validationSchemas');
// 注册路由 - 使用验证中间件验证请求数据,然后调用注册控制器
router.post('/register', validateRequest(registerSchema), register);
// 登录路由 - 使用验证中间件验证请求数据,然后调用登录控制器
router.post('/login', validateRequest(loginSchema), login);
// 获取当前用户信息路由 - 需要认证中间件保护,然后调用获取用户信息控制器
router.get('/me', protect, getMe);
module.exports = router;
OK,写到这系统应该支持以下功能
- 用户注册
- 用户登录
- 获取当前用户信息
- 请求验证
- JWT认证
先发出来,我要去测试下之前的代码和加下注释
测试:postman或者curl都行
POST http://localhost:3000/api/auth/register
Content-Type: application/json
{
"name": "测试用户",
"email": "test@example.com",
"password": "password123"
}
返回下列信息,恭喜你,您的接口可以用了
{
"_id": "68234d0c35c1531af74f8b21",
"username": "测试用户",
"email": "test@example.com",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY4MjM0ZDBjMzVjMTUzMWFmNzRmOGIyMSIsImlhdCI6MTc0NzE0Mzk0OSwiZXhwIjoxNzQ5NzM1OTQ5fQ.VGlahH93J-BGfgyq_aCTCEFV35hhA4ckag5MdISQsbE"
}
874

被折叠的 条评论
为什么被折叠?



