Node.js 开发实战:从入门到精通

Node.js 开发详解

一、Node.js 核心知识框架

1.1 基础概念

  • 什么是 Node.js:基于 Chrome V8 引擎构建的 JavaScript 运行环境
  • 事件驱动架构:非阻塞 I/O 模型
  • 单线程与事件循环:高效处理并发请求
  • NPM(Node 包管理器):Node.js 的默认包管理工具

1.2 核心模块与 API

  • 文件系统 (fs):文件操作
  • HTTP/HTTPS:创建 Web 服务器
  • 事件系统 (events):事件驱动编程
  • 流 (stream):处理流式数据
  • 路径 (path):处理文件路径
  • 进程 (process):进程信息与控制
  • 缓冲区 (buffer):处理二进制数据
  • 操作系统 (os):获取操作系统信息
  • 集群 (cluster):多进程能力

1.3 架构模式

  • CommonJS 模块系统:Node.js 中的模块机制
  • 回调函数与 Promise:异步处理方式
  • Async/Await:现代化异步语法
  • 中间件模式:Express 等框架的核心概念

1.4 Web 开发相关

  • RESTful API 设计
  • 数据库集成(MongoDB, MySQL, PostgreSQL)
  • 身份验证与授权
  • 错误处理机制
  • 日志记录

Node.js 博客系统架构图

系统整体架构

在这里插入图片描述

用户认证流程

ClientAuthMiddlewareUserControllerJWTDatabasePOST /api/users/register{username, email, password}检查用户是否存在返回检查结果创建新用户(密码自动加密)返回用户信息生成token返回token{token, user}POST /api/users/login{email, password}根据邮箱查找用户返回用户信息验证密码生成token返回token{token, user}GET /api/postsAuthorization: Bearer <token>验证token返回解码信息根据ID查找用户返回用户信息继续处理请求ClientAuthMiddlewareUserControllerJWTDatabase

文章操作流程

ClientPostControllerAuthMiddlewareDatabasePOST /api/posts{title, content, tags}验证通过创建文章(关联当前用户)返回文章信息{message, post}GET /api/posts?page=1&limit=10查询文章列表(分页, 填充作者信息)返回文章列表{posts, pagination}PUT /api/posts/:id{title, content}验证通过查找文章返回文章检查权限更新文章返回更新后的文章{message, post}DELETE /api/posts/:id验证通过查找文章返回文章检查权限删除文章删除成功{message: '删除成功'}ClientPostControllerAuthMiddlewareDatabase

Express 中间件执行顺序

graph LR
    A[Incoming Request] --> B[Application Level Middleware<br/>app.use(...)]
    B --> C[Route Middleware<br/>router.use(...)]
    C --> D[Route Handler<br/>app.get/post/put/delete]
    D --> E[Error Handling Middleware<br/>app.use(err, req, res, next)]
    E --> F[Response Sent to Client]

数据模型关系图

USERPOSTwrites

API 端点映射图

HTTP Methods
Users Routes
/api/users
Posts Routes
/api/posts
POST /register
用户注册
POST /login
用户登录
GET /me
获取当前用户信息
POST /
创建文章
GET /
获取文章列表
GET /:id
获取指定文章
PUT /:id
更新文章
DELETE /:id
删除文章
图例
需要认证: 橙色背景
公开接口: 绿色背景
认证接口: 蓝色背景

二、实战项目:简易博客系统

2.1 项目结构设计

blog-system/
├── app.js                 # 应用入口文件
├── package.json           # 项目配置文件
├── routes/                # 路由目录
│   ├── posts.js          # 文章路由
│   └── users.js          # 用户路由
├── controllers/           # 控制器目录
│   ├── postController.js # 文章控制器
│   └── userController.js # 用户控制器
├── models/                # 数据模型目录
│   ├── Post.js           # 文章模型
│   └── User.js           # 用户模型
├── middleware/            # 中间件目录
│   └── auth.js           # 认证中间件
└── utils/                 # 工具函数目录
    └── database.js       # 数据库连接

2.2 完整项目实现

package.json - 项目配置文件
{
  "name": "blog-system",
  "version": "1.0.0",
  "description": "A simple blog system built with Node.js",
  "main": "app.js",
  "scripts": {
    "start": "node app.js",
    "dev": "nodemon app.js"
  },
  "dependencies": {
    "express": "^4.18.2",
    "mongoose": "^7.5.0",
    "bcryptjs": "^2.4.3",
    "jsonwebtoken": "^9.0.2",
    "cors": "^2.8.5",
    "dotenv": "^16.3.1"
  },
  "devDependencies": {
    "nodemon": "^3.0.1"
  }
}
app.js - 应用入口文件
// 导入所需模块
const express = require('express');
const cors = require('cors');
require('dotenv').config();

// 创建 Express 应用实例
const app = express();
const PORT = process.env.PORT || 3000;

// 中间件配置
app.use(cors()); // 允许跨域请求
app.use(express.json()); // 解析 JSON 请求体
app.use(express.urlencoded({ extended: true })); // 解析 URL 编码的请求体

// 导入路由
const postRoutes = require('./routes/posts');
const userRoutes = require('./routes/users');

// 使用路由
app.use('/api/posts', postRoutes);
app.use('/api/users', userRoutes);

// 根路径欢迎页面
app.get('/', (req, res) => {
  res.json({
    message: '欢迎使用博客系统 API',
    version: '1.0.0',
    endpoints: {
      posts: '/api/posts',
      users: '/api/users'
    }
  });
});

// 错误处理中间件
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({
    message: '服务器内部错误',
    error: process.env.NODE_ENV === 'development' ? err.message : {}
  });
});

// 404 处理
app.use('*', (req, res) => {
  res.status(404).json({
    message: '页面未找到'
  });
});

// 启动服务器
app.listen(PORT, () => {
  console.log(`服务器运行在端口 ${PORT}`);
});

module.exports = app;
models/User.js - 用户数据模型
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

// 定义用户 Schema
const userSchema = new mongoose.Schema({
  username: {
    type: String,
    required: true,
    unique: true,
    trim: true,
    minlength: 3,
    maxlength: 30
  },
  email: {
    type: String,
    required: true,
    unique: true,
    trim: true,
    lowercase: true
  },
  password: {
    type: String,
    required: true,
    minlength: 6
  },
  createdAt: {
    type: Date,
    default: Date.now
  }
});

// 密码加密中间件(保存前执行)
userSchema.pre('save', async function(next) {
  // 如果密码没有被修改,则跳过加密
  if (!this.isModified('password')) return next();
  
  try {
    // 对密码进行哈希加密
    const salt = await bcrypt.genSalt(10);
    this.password = await bcrypt.hash(this.password, salt);
    next();
  } catch (error) {
    next(error);
  }
});

// 验证密码方法
userSchema.methods.comparePassword = async function(candidatePassword) {
  return await bcrypt.compare(candidatePassword, this.password);
};

// 创建并导出 User 模型
module.exports = mongoose.model('User', userSchema);
models/Post.js - 文章数据模型
const mongoose = require('mongoose');

// 定义文章 Schema
const postSchema = new mongoose.Schema({
  title: {
    type: String,
    required: true,
    trim: true,
    maxlength: 200
  },
  content: {
    type: String,
    required: true
  },
  author: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
    required: true
  },
  tags: [{
    type: String,
    trim: true
  }],
  published: {
    type: Boolean,
    default: false
  },
  createdAt: {
    type: Date,
    default: Date.now
  },
  updatedAt: {
    type: Date,
    default: Date.now
  }
});

// 更新时间中间件
postSchema.pre('save', function(next) {
  this.updatedAt = Date.now();
  next();
});

// 创建并导出 Post 模型
module.exports = mongoose.model('Post', postSchema);
controllers/userController.js - 用户控制器
const User = require('../models/User');
const jwt = require('jsonwebtoken');

// 生成 JWT token
const generateToken = (userId) => {
  return jwt.sign({ id: userId }, process.env.JWT_SECRET || 'default_secret', {
    expiresIn: '7d'
  });
};

// 用户注册
exports.register = async (req, res) => {
  try {
    const { username, email, password } = req.body;
    
    // 检查必填字段
    if (!username || !email || !password) {
      return res.status(400).json({
        message: '用户名、邮箱和密码都是必需的'
      });
    }
    
    // 检查用户是否已存在
    const existingUser = await User.findOne({
      $or: [{ email }, { username }]
    });
    
    if (existingUser) {
      return res.status(400).json({
        message: '用户名或邮箱已被使用'
      });
    }
    
    // 创建新用户
    const user = new User({
      username,
      email,
      password
    });
    
    await user.save();
    
    // 生成 token
    const token = generateToken(user._id);
    
    res.status(201).json({
      message: '用户注册成功',
      token,
      user: {
        id: user._id,
        username: user.username,
        email: user.email
      }
    });
  } catch (error) {
    res.status(500).json({
      message: '注册失败',
      error: error.message
    });
  }
};

// 用户登录
exports.login = async (req, res) => {
  try {
    const { email, password } = req.body;
    
    // 检查必填字段
    if (!email || !password) {
      return res.status(400).json({
        message: '邮箱和密码都是必需的'
      });
    }
    
    // 查找用户
    const user = await User.findOne({ email });
    if (!user) {
      return res.status(401).json({
        message: '邮箱或密码错误'
      });
    }
    
    // 验证密码
    const isMatch = await user.comparePassword(password);
    if (!isMatch) {
      return res.status(401).json({
        message: '邮箱或密码错误'
      });
    }
    
    // 生成 token
    const token = generateToken(user._id);
    
    res.json({
      message: '登录成功',
      token,
      user: {
        id: user._id,
        username: user.username,
        email: user.email
      }
    });
  } catch (error) {
    res.status(500).json({
      message: '登录失败',
      error: error.message
    });
  }
};

// 获取当前用户信息
exports.getCurrentUser = 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: error.message
    });
  }
};
controllers/postController.js - 文章控制器
const Post = require('../models/Post');

// 创建文章
exports.createPost = async (req, res) => {
  try {
    const { title, content, tags } = req.body;
    
    // 检查必填字段
    if (!title || !content) {
      return res.status(400).json({
        message: '标题和内容都是必需的'
      });
    }
    
    // 创建文章
    const post = new Post({
      title,
      content,
      tags: tags || [],
      author: req.user.id
    });
    
    await post.save();
    
    // 填充作者信息
    await post.populate('author', 'username email');
    
    res.status(201).json({
      message: '文章创建成功',
      post
    });
  } catch (error) {
    res.status(500).json({
      message: '创建文章失败',
      error: error.message
    });
  }
};

// 获取所有文章
exports.getAllPosts = async (req, res) => {
  try {
    const page = parseInt(req.query.page) || 1;
    const limit = parseInt(req.query.limit) || 10;
    const skip = (page - 1) * limit;
    
    // 查询条件
    const query = {};
    if (req.query.published === 'true') {
      query.published = true;
    }
    
    // 获取文章列表
    const posts = await Post.find(query)
      .populate('author', 'username')
      .sort({ createdAt: -1 })
      .skip(skip)
      .limit(limit);
    
    // 获取总数
    const total = await Post.countDocuments(query);
    
    res.json({
      posts,
      pagination: {
        current: page,
        pages: Math.ceil(total / limit),
        total
      }
    });
  } catch (error) {
    res.status(500).json({
      message: '获取文章列表失败',
      error: error.message
    });
  }
};

// 获取单篇文章
exports.getPostById = async (req, res) => {
  try {
    const post = await Post.findById(req.params.id)
      .populate('author', 'username email');
    
    if (!post) {
      return res.status(404).json({
        message: '文章不存在'
      });
    }
    
    res.json(post);
  } catch (error) {
    res.status(500).json({
      message: '获取文章失败',
      error: error.message
    });
  }
};

// 更新文章
exports.updatePost = async (req, res) => {
  try {
    const { title, content, tags, published } = req.body;
    
    // 查找文章
    const post = await Post.findById(req.params.id);
    
    if (!post) {
      return res.status(404).json({
        message: '文章不存在'
      });
    }
    
    // 检查权限(只有作者可以更新)
    if (post.author.toString() !== req.user.id) {
      return res.status(403).json({
        message: '无权修改此文章'
      });
    }
    
    // 更新文章
    post.title = title || post.title;
    post.content = content || post.content;
    post.tags = tags || post.tags;
    if (published !== undefined) {
      post.published = published;
    }
    
    await post.save();
    
    // 填充作者信息
    await post.populate('author', 'username email');
    
    res.json({
      message: '文章更新成功',
      post
    });
  } catch (error) {
    res.status(500).json({
      message: '更新文章失败',
      error: error.message
    });
  }
};

// 删除文章
exports.deletePost = async (req, res) => {
  try {
    const post = await Post.findById(req.params.id);
    
    if (!post) {
      return res.status(404).json({
        message: '文章不存在'
      });
    }
    
    // 检查权限(只有作者可以删除)
    if (post.author.toString() !== req.user.id) {
      return res.status(403).json({
        message: '无权删除此文章'
      });
    }
    
    await Post.findByIdAndDelete(req.params.id);
    
    res.json({
      message: '文章删除成功'
    });
  } catch (error) {
    res.status(500).json({
      message: '删除文章失败',
      error: error.message
    });
  }
};
middleware/auth.js - 认证中间件
const jwt = require('jsonwebtoken');
const User = require('../models/User');

// 认证中间件
const auth = async (req, res, next) => {
  try {
    // 从请求头获取 token
    const token = req.header('Authorization')?.replace('Bearer ', '');
    
    if (!token) {
      return res.status(401).json({
        message: '访问被拒绝,未提供认证令牌'
      });
    }
    
    // 验证 token
    const decoded = jwt.verify(token, process.env.JWT_SECRET || 'default_secret');
    
    // 查找用户
    const user = await User.findById(decoded.id).select('-password');
    
    if (!user) {
      return res.status(401).json({
        message: '认证失败,用户不存在'
      });
    }
    
    // 将用户信息添加到请求对象
    req.user = user;
    next();
  } catch (error) {
    res.status(401).json({
      message: '认证令牌无效'
    });
  }
};

module.exports = auth;
routes/users.js - 用户路由
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');
const auth = require('../middleware/auth');

// 注册路由 POST /api/users/register
router.post('/register', userController.register);

// 登录路由 POST /api/users/login
router.post('/login', userController.login);

// 获取当前用户信息 GET /api/users/me (需要认证)
router.get('/me', auth, userController.getCurrentUser);

module.exports = router;
routes/posts.js - 文章路由
const express = require('express');
const router = express.Router();
const postController = require('../controllers/postController');
const auth = require('../middleware/auth');

// 创建文章 POST /api/posts (需要认证)
router.post('/', auth, postController.createPost);

// 获取所有文章 GET /api/posts
router.get('/', postController.getAllPosts);

// 获取单篇文章 GET /api/posts/:id
router.get('/:id', postController.getPostById);

// 更新文章 PUT /api/posts/:id (需要认证)
router.put('/:id', auth, postController.updatePost);

// 删除文章 DELETE /api/posts/:id (需要认证)
router.delete('/:id', auth, postController.deletePost);

module.exports = router;

三、常用 API 详解

3.1 Express 核心 API

应用级方法
// 创建 Express 应用
const express = require('express');
const app = express();

// GET 请求
app.get('/', (req, res) => {
  res.send('Hello World!');
});

// POST 请求
app.post('/users', (req, res) => {
  res.send('Create a user');
});

// PUT 请求
app.put('/users/:id', (req, res) => {
  res.send('Update a user');
});

// DELETE 请求
app.delete('/users/:id', (req, res) => {
  res.send('Delete a user');
});

// 所有 HTTP 方法
app.all('/secret', (req, res, next) => {
  console.log('Accessing the secret section ...');
  next(); // 传递控制权到下一个处理器
});
路由参数
// 基本路由参数
app.get('/users/:userId/books/:bookId', (req, res) => {
  res.send(req.params); // { userId: '123', bookId: '456' }
});

// 正则表达式路由参数
app.get('/user/:userId(\\d+)', (req, res) => {
  res.send(`User ID: ${req.params.userId}`);
});
中间件使用
// 应用级中间件
app.use((req, res, next) => {
  console.log('Time:', Date.now());
  next();
});

// 路径特定中间件
app.use('/user/:id', (req, res, next) => {
  console.log('Request Type:', req.method);
  next();
});

// 错误处理中间件(必须有4个参数)
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

3.2 文件系统 (fs) API

读取文件
const fs = require('fs');

// 异步读取文件
fs.readFile('example.txt', 'utf8', (err, data) => {
  if (err) throw err;
  console.log(data);
});

// 同步读取文件
try {
  const data = fs.readFileSync('example.txt', 'utf8');
  console.log(data);
} catch (err) {
  console.error(err);
}

// Promise 方式读取文件
const fsPromises = require('fs').promises;

async function readFileAsync() {
  try {
    const data = await fsPromises.readFile('example.txt', 'utf8');
    console.log(data);
  } catch (err) {
    console.error(err);
  }
}
写入文件
const fs = require('fs');

// 异步写入文件
fs.writeFile('message.txt', 'Hello Node.js', (err) => {
  if (err) throw err;
  console.log('文件已保存!');
});

// 同步写入文件
try {
  fs.writeFileSync('message.txt', 'Hello Node.js');
  console.log('文件已保存!');
} catch (err) {
  console.error(err);
}

// 追加内容到文件
fs.appendFile('message.txt', '追加的数据', (err) => {
  if (err) throw err;
  console.log('数据已追加到文件');
});

3.3 HTTP 模块 API

创建 HTTP 服务器
const http = require('http');

// 创建简单服务器
const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});

server.listen(3000, '127.0.0.1', () => {
  console.log('服务器运行在 http://127.0.0.1:3000/');
});
处理不同请求方法
const http = require('http');
const url = require('url');

const server = http.createServer((req, res) => {
  const parsedUrl = url.parse(req.url, true);
  const path = parsedUrl.pathname;
  const method = req.method;
  
  // 设置响应头
  res.setHeader('Content-Type', 'application/json');
  
  if (method === 'GET' && path === '/api/users') {
    res.statusCode = 200;
    res.end(JSON.stringify({ message: 'Get all users' }));
  } else if (method === 'POST' && path === '/api/users') {
    let body = '';
    req.on('data', chunk => {
      body += chunk.toString();
    });
    req.on('end', () => {
      console.log(body);
      res.statusCode = 201;
      res.end(JSON.stringify({ message: 'User created' }));
    });
  } else {
    res.statusCode = 404;
    res.end(JSON.stringify({ message: 'Not Found' }));
  }
});

server.listen(3000);

3.4 事件系统 (events) API

EventEmitter 基本使用
const EventEmitter = require('events');

// 创建事件发射器实例
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();

// 监听事件
myEmitter.on('event', () => {
  console.log('事件被触发!');
});

// 发射事件
myEmitter.emit('event');

// 带参数的事件
myEmitter.on('data', (data) => {
  console.log('接收到数据:', data);
});

myEmitter.emit('data', { name: '张三', age: 25 });
一次性监听器
const EventEmitter = require('events');
const myEmitter = new EventEmitter();

// 只会触发一次
myEmitter.once('firstConnection', () => {
  console.log('第一次连接!');
});

myEmitter.emit('firstConnection'); // 输出: 第一次连接!
myEmitter.emit('firstConnection'); // 不输出任何内容

3.5 流 (stream) API

可读流
const fs = require('fs');

// 创建可读流
const readableStream = fs.createReadStream('large-file.txt');

readableStream.on('data', (chunk) => {
  console.log(`接收到 ${chunk.length} 字节的数据`);
});

readableStream.on('end', () => {
  console.log('数据读取完成');
});

readableStream.on('error', (err) => {
  console.error('发生错误:', err);
});
可写流
const fs = require('fs');

// 创建可写流
const writableStream = fs.createWriteStream('output.txt');

writableStream.write('Hello ');
writableStream.write('World!\n');
writableStream.end();

writableStream.on('finish', () => {
  console.log('写入完成');
});
管道流
const fs = require('fs');

// 使用管道将可读流连接到可写流
const readable = fs.createReadStream('input.txt');
const writable = fs.createWriteStream('output.txt');

readable.pipe(writable);

// 链式管道
const zlib = require('zlib');
const gzip = zlib.createGzip();

readable.pipe(gzip).pipe(fs.createWriteStream('output.txt.gz'));

四、总结

Node.js 是一个功能强大的 JavaScript 运行环境,适用于构建各种类型的服务器端应用程序。通过上述详细介绍和实际项目示例,我们可以看到:

  1. 核心概念清晰:事件驱动、非阻塞 I/O 是 Node.js 的核心优势
  2. 生态系统丰富:npm 提供了大量的第三方包
  3. 开发效率高:统一的语言栈减少上下文切换
  4. 性能优秀:适合 I/O 密集型应用

通过构建完整的博客系统项目,我们掌握了:

  • RESTful API 设计原则
  • 数据库集成(MongoDB)
  • 用户认证与授权
  • 错误处理机制
  • 中间件模式的应用

这些知识点构成了 Node.js 开发的基础,为更复杂的项目开发打下了坚实的基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FE_Jinger

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值