

文章目录
- [1. Node.js 全栈开发概述](#1. Node.js 全栈开发概述)
-
- [1.1 全栈开发的优势](#1.1 全栈开发的优势)
- [1.2 Node.js 全栈开发技术栈](#1.2 Node.js 全栈开发技术栈)
- [2. 开发环境搭建](#2. 开发环境搭建)
-
- [2.1 Node.js 和 npm 安装](#2.1 Node.js 和 npm 安装)
- [2.2 开发工具安装](#2.2 开发工具安装)
- [2.3 版本控制设置](#2.3 版本控制设置)
- [2.4 项目初始化流程](#2.4 项目初始化流程)
- [3. 后端开发 (Node.js)](#3. 后端开发 (Node.js))
-
- [3.1 Express 框架基础](#3.1 Express 框架基础)
- [3.2 RESTful API 设计](#3.2 RESTful API 设计)
- [3.3 中间件开发](#3.3 中间件开发)
- [3.4 数据库集成](#3.4 数据库集成)
- [3.5 身份验证和授权](#3.5 身份验证和授权)
- [3.6 后端架构](#3.6 后端架构)
- [4. 前端开发](#4. 前端开发)
-
- [4.1 React 基础](#4.1 React 基础)
- [4.2 状态管理 (Redux)](#4.2 状态管理 (Redux))
- [4.3 API 请求处理](#4.3 API 请求处理)
- [4.4 前端路由 (React Router)](#4.4 前端路由 (React Router))
- [4.5 前端架构](#4.5 前端架构)
- [5. 全栈集成](#5. 全栈集成)
-
- [5.1 前后端通信](#5.1 前后端通信)
- [5.2 身份验证流程](#5.2 身份验证流程)
- [5.3 完整的全栈应用架构](#5.3 完整的全栈应用架构)
- [6. 数据建模与持久化](#6. 数据建模与持久化)
-
- [6.1 MongoDB 数据模型](#6.1 MongoDB 数据模型)
- [6.2 MySQL 数据建模](#6.2 MySQL 数据建模)
- [6.3 数据关系建模](#6.3 数据关系建模)
- [7. API 开发与设计模式](#7. API 开发与设计模式)
-
- [7.1 模块化路由](#7.1 模块化路由)
- [7.2 控制器模式](#7.2 控制器模式)
- [7.3 服务层模式](#7.3 服务层模式)
- [7.4 错误处理模式](#7.4 错误处理模式)
- [8. 安全性与性能优化](#8. 安全性与性能优化)
-
- [8.1 安全最佳实践](#8.1 安全最佳实践)
- [8.2 性能优化技术](#8.2 性能优化技术)
- [8.3 日志系统](#8.3 日志系统)
- [9. 部署与 DevOps](#9. 部署与 DevOps)
-
- [9.1 Docker 容器化](#9.1 Docker 容器化)
- [9.2 CI/CD 流水线](#9.2 CI/CD 流水线)
- [9.3 部署流程](#9.3 部署流程)
正文
1. Node.js 全栈开发概述
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,使开发者能够使用 JavaScript 进行服务器端编程。全栈开发指的是同时处理前端和后端的开发工作。
1.1 全栈开发的优势
- 使用统一的语言(JavaScript)开发前后端
- 减少技术栈上下文切换成本
- 提高开发效率和代码复用性
- 便于小团队快速构建完整应用
- 简化部署和维护流程
1.2 Node.js 全栈开发技术栈
Node.js全栈技术栈 前端技术 后端技术 数据库技术 开发工具 部署技术 React/Vue/Angular HTML/CSS TypeScript Express/Koa/NestJS GraphQL RESTful API MongoDB MySQL/PostgreSQL Redis npm/Yarn Webpack/Vite Git Docker AWS/Azure/GCP CI/CD管道
2. 开发环境搭建
2.1 Node.js 和 npm 安装
Node.js 是全栈开发的基础环境,npm 是其包管理工具。
bash
# 安装 Node.js 和 npm (macOS)
brew install node
# 安装 Node.js 和 npm (Ubuntu)
sudo apt update
sudo apt install nodejs npm
# 安装 Node.js 和 npm (Windows)
# 从 https://nodejs.org 下载安装程序
# 验证安装
node --version
npm --version
2.2 开发工具安装
良好的开发工具可以提高开发效率:
bash
# 安装常用全局工具
npm install -g nodemon # 自动重启服务器
npm install -g ts-node # TypeScript 执行环境
npm install -g create-react-app # React 项目生成器
npm install -g @vue/cli # Vue 项目生成器
npm install -g prettier # 代码格式化工具
2.3 版本控制设置
Git 是最流行的版本控制系统:
bash
# 初始化 Git 仓库
git init
# 创建 .gitignore 文件
echo "node_modules/\n.env\n.DS_Store\ndist/\nbuild/" > .gitignore
# 配置用户信息
git config --global user.name "Your Name"
git config --global user.email "[email protected]"
2.4 项目初始化流程
项目初始化 创建项目目录 初始化npm 创建基本目录结构 安装依赖 配置环境变量 编写启动脚本 初始化Git仓库
3. 后端开发 (Node.js)
3.1 Express 框架基础
Express 是 Node.js 最流行的 Web 应用框架之一。
javascript
// 基本 Express 服务器
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
// 中间件
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 路由
app.get('/', (req, res) => {
res.send('Hello World!');
});
// 启动服务器
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
3.2 RESTful API 设计
RESTful API 是一种软件架构风格,用于设计网络应用。
javascript
// 用户资源的 RESTful API
const express = require('express');
const router = express.Router();
// 获取所有用户
router.get('/users', (req, res) => {
// 实现获取所有用户的逻辑
res.json({ users: [] });
});
// 获取单个用户
router.get('/users/:id', (req, res) => {
const userId = req.params.id;
// 实现获取单个用户的逻辑
res.json({ user: { id: userId } });
});
// 创建用户
router.post('/users', (req, res) => {
const userData = req.body;
// 实现创建用户的逻辑
res.status(201).json({ user: userData });
});
// 更新用户
router.put('/users/:id', (req, res) => {
const userId = req.params.id;
const userData = req.body;
// 实现更新用户的逻辑
res.json({ user: { id: userId, ...userData } });
});
// 删除用户
router.delete('/users/:id', (req, res) => {
const userId = req.params.id;
// 实现删除用户的逻辑
res.status(204).end();
});
module.exports = router;
3.3 中间件开发
中间件是 Express 中处理请求和响应的功能模块。
javascript
// 认证中间件
const jwt = require('jsonwebtoken');
const authMiddleware = (req, res, next) => {
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ message: '未提供访问令牌' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({ message: '无效的访问令牌' });
}
};
// 错误处理中间件
const errorHandlerMiddleware = (err, req, res, next) => {
console.error(err.stack);
// 自定义错误响应
if (err.name === 'ValidationError') {
return res.status(400).json({ message: err.message });
}
res.status(500).json({ message: '服务器内部错误' });
};
module.exports = { authMiddleware, errorHandlerMiddleware };
3.4 数据库集成
Node.js 可以与各种数据库集成。
javascript
// MongoDB 集成 (使用 Mongoose)
const mongoose = require('mongoose');
// 连接 MongoDB
mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log('MongoDB 连接成功'))
.catch((err) => console.error('MongoDB 连接失败:', err));
// 定义用户模型
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,
},
createdAt: {
type: Date,
default: Date.now,
},
});
const User = mongoose.model('User', userSchema);
module.exports = User;
javascript
// MySQL 集成 (使用 mysql2)
const mysql = require('mysql2/promise');
// 创建连接池
const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0,
});
// 用户数据访问对象
const UserDAO = {
// 获取所有用户
async getAllUsers() {
const [rows] = await pool.query('SELECT * FROM users');
return rows;
},
// 通过 ID 获取用户
async getUserById(id) {
const [rows] = await pool.query('SELECT * FROM users WHERE id = ?', [id]);
return rows[0];
},
// 创建用户
async createUser(user) {
const { username, email, password } = user;
const [result] = await pool.query(
'INSERT INTO users (username, email, password) VALUES (?, ?, ?)',
[username, email, password]
);
return { id: result.insertId, ...user };
},
};
module.exports = UserDAO;
3.5 身份验证和授权
javascript
// JWT 身份验证实现
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const User = require('../models/User');
const authController = {
// 用户注册
async register(req, res) {
try {
const { username, email, password } = req.body;
// 检查是否已存在用户
const existingUser = await User.findOne({ $or: [{ username }, { email }] });
if (existingUser) {
return res.status(400).json({ message: '用户名或邮箱已被使用' });
}
// 哈希密码
const hashedPassword = await bcrypt.hash(password, 10);
// 创建新用户
const newUser = new User({
username,
email,
password: hashedPassword,
});
await newUser.save();
// 生成 JWT
const token = jwt.sign(
{ userId: newUser._id, username: newUser.username },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
res.status(201).json({ user: newUser, token });
} catch (error) {
res.status(500).json({ message: '注册失败', error: error.message });
}
},
// 用户登录
async login(req, res) {
try {
const { username, password } = req.body;
// 查找用户
const user = await User.findOne({ username });
if (!user) {
return res.status(401).json({ message: '用户名或密码错误' });
}
// 验证密码
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
return res.status(401).json({ message: '用户名或密码错误' });
}
// 生成 JWT
const token = jwt.sign(
{ userId: user._id, username: user.username },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
res.json({ user, token });
} catch (error) {
res.status(500).json({ message: '登录失败', error: error.message });
}
},
};
module.exports = authController;
3.6 后端架构
Express 应用 路由层 中间件层 控制器层 数据访问层 服务层 API 路由 Web 路由 认证路由 身份验证 日志记录 错误处理 CORS 用户控制器 产品控制器 订单控制器 用户仓库 产品仓库 订单仓库 认证服务 电子邮件服务 支付服务 数据库
4. 前端开发
4.1 React 基础
React 是一个用于构建用户界面的 JavaScript 库。
javascript
// 创建 React 应用
npx create-react-app my-app
cd my-app
npm start
// 简单的 React 组件
import React from 'react';
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
export default Welcome;
4.2 状态管理 (Redux)
Redux 是一个 JavaScript 状态容器,提供可预测的状态管理。
javascript
// Redux store 配置
import { createStore, applyMiddleware, combineReducers } from 'redux';
import thunk from 'redux-thunk';
import { userReducer } from './reducers/userReducer';
import { productReducer } from './reducers/productReducer';
// 合并 reducers
const rootReducer = combineReducers({
user: userReducer,
products: productReducer,
});
// 创建 store
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
export default store;
// 用户 reducer
const initialState = {
currentUser: null,
isLoading: false,
error: null,
};
export const userReducer = (state = initialState, action) => {
switch (action.type) {
case 'LOGIN_REQUEST':
return { ...state, isLoading: true, error: null };
case 'LOGIN_SUCCESS':
return { ...state, isLoading: false, currentUser: action.payload };
case 'LOGIN_FAILURE':
return { ...state, isLoading: false, error: action.payload };
case 'LOGOUT':
return { ...state, currentUser: null };
default:
return state;
}
};
// 用户 actions
export const loginUser = (credentials) => {
return async (dispatch) => {
try {
dispatch({ type: 'LOGIN_REQUEST' });
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}
const data = await response.json();
localStorage.setItem('token', data.token);
dispatch({ type: 'LOGIN_SUCCESS', payload: data.user });
} catch (error) {
dispatch({ type: 'LOGIN_FAILURE', payload: error.message });
}
};
};
4.3 API 请求处理
使用 Axios 或 Fetch API 处理 HTTP 请求。
javascript
// 使用 Axios 的 API 服务
import axios from 'axios';
// 创建 axios 实例
const apiClient = axios.create({
baseURL: process.env.REACT_APP_API_URL,
headers: {
'Content-Type': 'application/json',
},
});
// 请求拦截器添加认证 token
apiClient.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器处理错误
apiClient.interceptors.response.use(
(response) => response,
(error) => {
// 处理 401 错误
if (error.response && error.response.status === 401) {
// 清除本地存储并重定向到登录页
localStorage.removeItem('token');
window.location.href = '/login';
}
return Promise.reject(error);
}
);
// 用户相关 API 调用
export const userService = {
login: (credentials) => apiClient.post('/auth/login', credentials),
register: (userData) => apiClient.post('/auth/register', userData),
getCurrentUser: () => apiClient.get('/users/me'),
updateProfile: (userData) => apiClient.put('/users/me', userData),
};
// 产品相关 API 调用
export const productService = {
getProducts: (params) => apiClient.get('/products', { params }),
getProductById: (id) => apiClient.get(`/products/${id}`),
createProduct: (productData) => apiClient.post('/products', productData),
updateProduct: (id, productData) => apiClient.put(`/products/${id}`, productData),
deleteProduct: (id) => apiClient.delete(`/products/${id}`),
};
export default apiClient;
4.4 前端路由 (React Router)
React Router 是 React 应用程序的声明式路由。
javascript
// React Router 配置
import React from 'react';
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import { useSelector } from 'react-redux';
// 组件导入
import Home from './pages/Home';
import Login from './pages/Login';
import Register from './pages/Register';
import Dashboard from './pages/Dashboard';
import Profile from './pages/Profile';
import Products from './pages/Products';
import ProductDetail from './pages/ProductDetail';
import NotFound from './pages/NotFound';
// 私有路由组件
const PrivateRoute = ({ children }) => {
const { currentUser } = useSelector((state) => state.user);
return currentUser ? children : <Navigate to="/login" />;
};
const AppRouter = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
{/* 受保护的路由 */}
<Route path="/dashboard" element={
<PrivateRoute>
<Dashboard />
</PrivateRoute>
} />
<Route path="/profile" element={
<PrivateRoute>
<Profile />
</PrivateRoute>
} />
<Route path="/products" element={<Products />} />
<Route path="/products/:id" element={<ProductDetail />} />
{/* 404 页面 */}
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
};
export default AppRouter;
4.5 前端架构
React 应用 组件层 状态管理层 服务层 路由层 工具层 页面组件 UI组件 布局组件 表单组件 Redux Store Actions Reducers Middleware API 服务 认证服务 存储服务 公共路由 私有路由 路由守卫 工具函数 自定义钩子 常量
5. 全栈集成
5.1 前后端通信
浏览器客户端 React 前端 Express API 数据库 用户操作 HTTP请求 发送JWT令牌进行身份验证 查询/操作数据 返回数据 JSON响应 包含状态码和数据 更新UI 浏览器客户端 React 前端 Express API 数据库
5.2 身份验证流程
用户 前端 (React) 后端 (Express) 数据库 输入登录凭据 POST /api/auth/login 验证用户凭据 返回用户数据 生成JWT令牌 返回令牌和用户信息 存储令牌(localStorage) 后续授权请求 发送请求(带Authorization头) 验证JWT令牌 返回受保护的资源 令牌过期 发送请求(带过期令牌) 返回401未授权 清除令牌并重定向到登录页 用户 前端 (React) 后端 (Express) 数据库
5.3 完整的全栈应用架构
基础设施 持久化层 数据层 API层 前端层 服务器 - AWS/Azure CI/CD - Jenkins/GitHub Actions 监控 - Prometheus 日志 - ELK Stack MongoDB/MySQL 文件存储 数据模型 数据访问对象 ORM - Mongoose/Sequelize 缓存 - Redis Express 服务器 路由控制器 中间件 认证服务 业务逻辑 React 应用 状态管理 - Redux 路由 - React Router UI库 - Material UI API 客户端 - Axios
6. 数据建模与持久化
6.1 MongoDB 数据模型
javascript
// 用户模型
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
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,
match: [/^\S+@\S+\.\S+$/, '请提供有效的电子邮件地址'],
},
password: {
type: String,
required: true,
minlength: 6,
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user',
},
profilePicture: String,
bio: {
type: String,
maxlength: 500,
},
isActive: {
type: Boolean,
default: true,
},
lastLogin: Date,
}, {
timestamps: true,
});
// 密码哈希中间件
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 bcrypt.compare(candidatePassword, this.password);
};
const User = mongoose.model('User', userSchema);
module.exports = User;
6.2 MySQL 数据建模
sql
-- 用户表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(30) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
role ENUM('user', 'admin') DEFAULT 'user',
profile_picture VARCHAR(255),
bio TEXT,
is_active BOOLEAN DEFAULT TRUE,
last_login DATETIME,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 产品表
CREATE TABLE products (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
description TEXT,
price DECIMAL(10, 2) NOT NULL,
stock INT NOT NULL DEFAULT 0,
category_id INT,
image_url VARCHAR(255),
is_featured BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL
);
-- 类别表
CREATE TABLE categories (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL UNIQUE,
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 订单表
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
status ENUM('pending', 'processing', 'shipped', 'delivered', 'cancelled') DEFAULT 'pending',
total_amount DECIMAL(10, 2) NOT NULL,
shipping_address TEXT NOT NULL,
payment_method VARCHAR(50) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- 订单项表
CREATE TABLE order_items (
id INT AUTO_INCREMENT PRIMARY KEY,
order_id INT NOT NULL,
product_id INT NOT NULL,
quantity INT NOT NULL,
price DECIMAL(10, 2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE RESTRICT
);
-- 用户地址表
CREATE TABLE user_addresses (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
address_line1 VARCHAR(100) NOT NULL,
address_line2 VARCHAR(100),
city VARCHAR(50) NOT NULL,
state VARCHAR(50) NOT NULL,
postal_code VARCHAR(20) NOT NULL,
country VARCHAR(50) NOT NULL,
is_default BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- 产品评论表
CREATE TABLE product_reviews (
id INT AUTO_INCREMENT PRIMARY KEY,
product_id INT NOT NULL,
user_id INT NOT NULL,
rating INT NOT NULL CHECK (rating BETWEEN 1 AND 5),
comment TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE (product_id, user_id)
);
6.3 数据关系建模
USERS int id PK string username string email string password enum role string profile_picture text bio boolean is_active datetime last_login timestamp created_at timestamp updated_at ORDERS int id PK int user_id FK enum status decimal total_amount text shipping_address string payment_method timestamp created_at timestamp updated_at USER_ADDRESSES int id PK int user_id FK string address_line1 string address_line2 string city string state string postal_code string country boolean is_default timestamp created_at timestamp updated_at PRODUCT_REVIEWS int id PK int product_id FK int user_id FK int rating text comment timestamp created_at timestamp updated_at CATEGORIES int id PK string name text description timestamp created_at timestamp updated_at PRODUCTS int id PK string name text description decimal price int stock int category_id FK string image_url boolean is_featured timestamp created_at timestamp updated_at ORDER_ITEMS int id PK int order_id FK int product_id FK int quantity decimal price timestamp created_at timestamp updated_at places has writes contains receives includes contains
7. API 开发与设计模式
7.1 模块化路由
javascript
// /routes/index.js - 路由主入口
const express = require('express');
const userRoutes = require('./userRoutes');
const productRoutes = require('./productRoutes');
const orderRoutes = require('./orderRoutes');
const authRoutes = require('./authRoutes');
const router = express.Router();
// API 版本控制
router.use('/v1/users', userRoutes);
router.use('/v1/products', productRoutes);
router.use('/v1/orders', orderRoutes);
router.use('/v1/auth', authRoutes);
module.exports = router;
// /routes/userRoutes.js - 用户相关路由
const express = require('express');
const userController = require('../controllers/userController');
const { authMiddleware, adminMiddleware } = require('../middleware/authMiddleware');
const router = express.Router();
// 公共路由
router.get('/:id/profile', userController.getUserProfile);
// 需要认证的路由
router.get('/me', authMiddleware, userController.getCurrentUser);
router.put('/me', authMiddleware, userController.updateProfile);
router.get('/me/orders', authMiddleware, userController.getUserOrders);
// 管理员路由
router.get('/', authMiddleware, adminMiddleware, userController.getAllUsers);
router.delete('/:id', authMiddleware, adminMiddleware, userController.deleteUser);
module.exports = router;
7.2 控制器模式
javascript
// /controllers/productController.js
const Product = require('../models/Product');
const { createError } = require('../utils/errorHandler');
// 控制器对象
const productController = {
// 获取所有产品
async getAllProducts(req, res, next) {
try {
const { category, search, minPrice, maxPrice, sort, limit = 10, page = 1 } = req.query;
// 构建查询条件
const query = {};
if (category) {
query.category_id = category;
}
if (search) {
query.name = { $regex: search, $options: 'i' };
}
if (minPrice || maxPrice) {
query.price = {};
if (minPrice) query.price.$gte = parseFloat(minPrice);
if (maxPrice) query.price.$lte = parseFloat(maxPrice);
}
// 构建排序条件
let sortOptions = { createdAt: -1 }; // 默认按创建时间降序
if (sort) {
const [field, order] = sort.split(':');
sortOptions = { [field]: order === 'desc' ? -1 : 1 };
}
// 计算分页
const skip = (parseInt(page) - 1) * parseInt(limit);
// 执行查询
const products = await Product.find(query)
.sort(sortOptions)
.limit(parseInt(limit))
.skip(skip);
// 获取总数
const total = await Product.countDocuments(query);
res.json({
products,
pagination: {
total,
page: parseInt(page),
limit: parseInt(limit),
pages: Math.ceil(total / parseInt(limit)),
},
});
} catch (error) {
next(error);
}
},
// 获取单个产品
async getProductById(req, res, next) {
try {
const { id } = req.params;
const product = await Product.findById(id);
if (!product) {
return next(createError(404, '找不到该产品'));
}
res.json(product);
} catch (error) {
next(error);
}
},
// 创建产品
async createProduct(req, res, next) {
try {
const productData = req.body;
const newProduct = new Product(productData);
await newProduct.save();
res.status(201).json(newProduct);
} catch (error) {
next(error);
}
},
// 更新产品
async updateProduct(req, res, next) {
try {
const { id } = req.params;
const updateData = req.body;
const product = await Product.findByIdAndUpdate(
id,
updateData,
{ new: true, runValidators: true }
);
if (!product) {
return next(createError(404, '找不到该产品'));
}
res.json(product);
} catch (error) {
next(error);
}
},
// 删除产品
async deleteProduct(req, res, next) {
try {
const { id } = req.params;
const product = await Product.findByIdAndDelete(id);
if (!product) {
return next(createError(404, '找不到该产品'));
}
res.status(204).end();
} catch (error) {
next(error);
}
},
};
module.exports = productController;
7.3 服务层模式
javascript
// /services/orderService.js
const Order = require('../models/Order');
const Product = require('../models/Product');
const { createError } = require('../utils/errorHandler');
// 订单服务
const orderService = {
// 创建订单
async createOrder(orderData, userId) {
// 验证产品库存
const orderItems = orderData.items;
let totalAmount = 0;
// 验证所有产品是否有足够库存
for (const item of orderItems) {
const product = await Product.findById(item.productId);
if (!product) {
throw createError(404, `商品ID ${item.productId} 不存在`);
}
if (product.stock < item.quantity) {
throw createError(400, `商品 ${product.name} 库存不足`);
}
// 计算项目价格
item.price = product.price;
totalAmount += product.price * item.quantity;
}
// 创建订单事务
const session = await Order.startSession();
session.startTransaction();
try {
// 创建订单
const newOrder = new Order({
user: userId,
items: orderItems.map(item => ({
product: item.productId,
quantity: item.quantity,
price: item.price
})),
totalAmount,
shippingAddress: orderData.shippingAddress,
paymentMethod: orderData.paymentMethod,
status: 'pending'
});
await newOrder.save({ session });
// 更新产品库存
for (const item of orderItems) {
await Product.findByIdAndUpdate(
item.productId,
{ $inc: { stock: -item.quantity } },
{ session }
);
}
// 提交事务
await session.commitTransaction();
session.endSession();
return newOrder;
} catch (error) {
// 回滚事务
await session.abortTransaction();
session.endSession();
throw error;
}
},
// 获取用户订单
async getUserOrders(userId) {
return Order.find({ user: userId })
.sort({ createdAt: -1 })
.populate('items.product', 'name image_url');
},
// 获取订单详情
async getOrderById(orderId, userId, isAdmin = false) {
const query = { _id: orderId };
// 如果不是管理员,只能查看自己的订单
if (!isAdmin) {
query.user = userId;
}
const order = await Order.findOne(query)
.populate('user', 'username email')
.populate('items.product');
if (!order) {
throw createError(404, '订单不存在');
}
return order;
},
// 更新订单状态
async updateOrderStatus(orderId, status, userId, isAdmin = false) {
const query = { _id: orderId };
// 如果不是管理员,只能更新自己的订单
if (!isAdmin) {
query.user = userId;
// 非管理员只能取消订单
if (status !== 'cancelled') {
throw createError(403, '没有权限执行此操作');
}
// 只能取消待处理或处理中的订单
query.status = { $in: ['pending', 'processing'] };
}
const order = await Order.findOneAndUpdate(
query,
{ status },
{ new: true, runValidators: true }
);
if (!order) {
throw createError(404, '订单不存在或无法更新状态');
}
return order;
},
};
module.exports = orderService;
7.4 错误处理模式
javascript
// /utils/errorHandler.js
// 创建自定义错误
const createError = (statusCode, message, details = null) => {
const error = new Error(message);
error.statusCode = statusCode;
error.details = details;
return error;
};
// 全局错误处理中间件
const errorHandler = (err, req, res, next) => {
console.error('Error:', err);
// 获取错误状态码和消息
const statusCode = err.statusCode || 500;
const message = err.message || '服务器内部错误';
// 区分不同环境下的错误响应
const response = {
error: {
message,
status: statusCode,
},
};
// 在开发环境中提供更详细的错误信息
if (process.env.NODE_ENV === 'development') {
response.error.stack = err.stack;
if (err.details) {
response.error.details = err.details;
}
}
// MongoDB 验证错误处理
if (err.name === 'ValidationError') {
response.error.status = 400;
response.error.message = '数据验证失败';
response.error.details = Object.values(err.errors).map(e => e.message);
}
// MongoDB 重复键错误处理
if (err.code === 11000) {
response.error.status = 409;
response.error.message = '数据已存在';
const field = Object.keys(err.keyValue)[0];
response.error.details = `${field} 已被使用`;
}
// JWT 错误处理
if (err.name === 'JsonWebTokenError') {
response.error.status = 401;
response.error.message = '无效的认证令牌';
}
if (err.name === 'TokenExpiredError') {
response.error.status = 401;
response.error.message = '认证令牌已过期';
}
res.status(response.error.status).json(response);
};
module.exports = { createError, errorHandler };
8. 安全性与性能优化
8.1 安全最佳实践
javascript
// /config/security.js
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');
const hpp = require('hpp');
const cors = require('cors');
// 配置安全中间件
const configSecurity = (app) => {
// 设置安全 HTTP 头
app.use(helmet());
// 防止 XSS 攻击
app.use(xss());
// 防止 NoSQL 注入
app.use(mongoSanitize());
// 防止参数污染
app.use(hpp());
// 配置 CORS
app.use(cors({
origin: process.env.CORS_ORIGIN || '*',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
}));
// 配置速率限制
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100, // 每个IP在windowMs内最多100个请求
message: {
error: {
message: '请求过多,请稍后再试',
status: 429,
},
},
});
// 应用速率限制到所有请求
app.use('/api', limiter);
// 严格的认证路由限制
const authLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1小时
max: 10, // 每个IP每小时最多10次尝试
message: {
error: {
message: '尝试次数过多,请稍后再试',
status: 429,
},
},
});
// 应用认证限制到登录和注册路由
app.use('/api/v1/auth/login', authLimiter);
app.use('/api/v1/auth/register', authLimiter);
};
module.exports = configSecurity;
8.2 性能优化技术
javascript
// /config/performance.js
const compression = require('compression');
const { createClient } = require('redis');
// Redis 客户端
let redisClient;
// 配置性能优化
const configPerformance = async (app) => {
// 启用 gzip 压缩
app.use(compression());
// 设置 Redis 缓存(如果配置了)
if (process.env.REDIS_URL) {
redisClient = createClient({
url: process.env.REDIS_URL,
});
await redisClient.connect().catch(err => {
console.error('Redis 连接失败:', err);
});
redisClient.on('error', (err) => {
console.error('Redis 错误:', err);
});
console.log('Redis 缓存已启用');
}
};
// 缓存中间件
const cacheMiddleware = (duration) => {
return async (req, res, next) => {
// 如果 Redis 未连接,跳过缓存
if (!redisClient || !redisClient.isReady) {
return next();
}
// 跳过非 GET 请求的缓存
if (req.method !== 'GET') {
return next();
}
// 创建缓存键
const cacheKey = `api:${req.originalUrl}`;
try {
// 尝试从缓存获取数据
const cachedData = await redisClient.get(cacheKey);
if (cachedData) {
// 返回缓存数据
const data = JSON.parse(cachedData);
return res.json(data);
}
// 修改 res.json 方法以缓存响应
const originalJson = res.json;
res.json = function(data) {
// 将数据保存到缓存
redisClient.setEx(cacheKey, duration, JSON.stringify(data))
.catch(err => console.error('Redis 缓存错误:', err));
// 调用原始 json 方法
return originalJson.call(this, data);
};
next();
} catch (error) {
console.error('缓存错误:', error);
next();
}
};
};
// 清除缓存模式
const clearCache = async (pattern) => {
if (!redisClient || !redisClient.isReady) {
return;
}
try {
// 查找匹配的键
const keys = await redisClient.keys(pattern);
// 如果有匹配的键,删除它们
if (keys.length > 0) {
await redisClient.del(keys);
console.log(`已清除 ${keys.length} 个缓存键`);
}
} catch (error) {
console.error('清除缓存错误:', error);
}
};
module.exports = {
configPerformance,
cacheMiddleware,
clearCache,
};
8.3 日志系统
javascript
// /utils/logger.js
const winston = require('winston');
const { format, transports, createLogger } = winston;
const path = require('path');
// 定义日志格式
const logFormat = format.combine(
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
format.errors({ stack: true }),
format.splat(),
format.json()
);
// 创建 logger 实例
const logger = createLogger({
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
format: logFormat,
defaultMeta: { service: 'api-service' },
transports: [
// 控制台输出
new transports.Console({
format: format.combine(
format.colorize(),
format.printf(
info => `${info.timestamp} ${info.level}: ${info.message}${info.stack ? '\n' + info.stack : ''}`
)
),
}),
],
});
// 在生产环境中添加文件传输
if (process.env.NODE_ENV === 'production') {
logger.add(
new transports.File({
filename: path.join(__dirname, '../logs/error.log'),
level: 'error',
maxsize: 10485760, // 10MB
maxFiles: 5,
})
);
logger.add(
new transports.File({
filename: path.join(__dirname, '../logs/combined.log'),
maxsize: 10485760, // 10MB
maxFiles: 5,
})
);
}
// 捕获未处理的异常和拒绝
logger.exceptions.handle(
new transports.File({ filename: path.join(__dirname, '../logs/exceptions.log') })
);
// 中间件:请求日志
const requestLogger = (req, res, next) => {
const startTime = new Date();
// 请求完成时的回调
res.on('finish', () => {
const duration = new Date() - startTime;
logger.info({
type: 'request',
method: req.method,
path: req.path,
query: req.query,
statusCode: res.statusCode,
duration: `${duration}ms`,
userAgent: req.get('User-Agent'),
ip: req.ip,
});
});
next();
};
module.exports = { logger, requestLogger };
9. 部署与 DevOps
9.1 Docker 容器化
dockerfile
# Dockerfile
FROM node:18-alpine
# 创建应用目录
WORKDIR /usr/src/app
# 安装应用依赖
COPY package*.json ./
RUN npm ci --only=production
# 复制应用代码
COPY . .
# 设置环境变量
ENV NODE_ENV=production
ENV PORT=3000
# 暴露端口
EXPOSE 3000
# 启动应用
CMD ["node", "server.js"]
yaml
# docker-compose.yml
version: '3'
services:
app:
build: .
restart: always
ports:
- "3000:3000"
depends_on:
- mongodb
- redis
environment:
- NODE_ENV=production
- PORT=3000
- MONGODB_URI=mongodb://mongodb:27017/myapp
- REDIS_URL=redis://redis:6379
- JWT_SECRET=your_jwt_secret
- CORS_ORIGIN=https://yourdomain.com
volumes:
- ./logs:/usr/src/app/logs
networks:
- app-network
mongodb:
image: mongo:6
restart: always
ports:
- "27017:27017"
volumes:
- mongodb-data:/data/db
networks:
- app-network
redis:
image: redis:7-alpine
restart: always
ports:
- "6379:6379"
volumes:
- redis-data:/data
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
mongodb-data:
redis-data:
9.2 CI/CD 流水线
yaml
# .github/workflows/main.yml
name: Node.js CI/CD
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x, 18.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linting
run: npm run lint
- name: Run tests
run: npm test
build-and-deploy:
needs: test
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Use Node.js 18.x
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: yourusername/yourapp:latest
- name: Deploy to server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /path/to/your/app
docker-compose pull
docker-compose up -d
9.3 部署流程
代码提交 自动化测试 失败 成功 失败 成功 是 否 开发阶段 CI/CD 流水线 测试通过? 通知开发者 构建 Docker 镜像 推送到容器仓库 部署到测试环境 性能和验收测试 部署到生产环境 监控系统性能 发现问题? 回滚部署 完成部署
结语
感谢您的阅读!期待您的一键三连!欢迎指正!
