博客项目全栈开发 API接口 第二章:环境搭建,注册/登录

前言

默认大家已经看过前面的文章,并成功搭建项目。这章主要写项目的环境搭建,比如基础结构文件,配置数据库,注册登录功能。注册登录功能放这章,是因为要用他来写测试方法。文件你们对照着创建,代码文件我等会来加注释,写到具体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"
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值