Express.js
Express.js 是一个快速、灵活、极简的 Node.js Web 应用程序框架,为 Web 和移动应用程序提供了一组强大的功能。它是最流行的 Node.js Web 框架,也是许多其他流行框架的基础。
1. 基础使用
1.1 安装和设置
# 创建新项目
mkdir myapp
cd myapp
npm init -y
# 安装 Express
npm install express
# 安装常用中间件
npm install body-parser cookie-parser morgan cors helmet
1.2 基本应用
// app.js
const express = require('express');
const app = express();
const port = 3000;
// 中间件
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 路由
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.post('/api/users', (req, res) => {
const user = req.body;
res.json(user);
});
// 启动服务器
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
2. 路由系统
2.1 基础路由
// routes/users.js
const express = require('express');
const router = express.Router();
// GET /users
router.get('/', (req, res) => {
res.json([{ id: 1, name: 'John' }]);
});
// GET /users/:id
router.get('/:id', (req, res) => {
const id = req.params.id;
res.json({ id, name: 'John' });
});
// POST /users
router.post('/', (req, res) => {
const user = req.body;
res.status(201).json(user);
});
module.exports = router;
// app.js
const usersRouter = require('./routes/users');
app.use('/users', usersRouter);
2.2 高级路由
// 路由参数
app.get('/users/:userId/books/:bookId', (req, res) => {
res.json(req.params);
});
// 正则表达式路由
app.get(/.*fly$/, (req, res) => {
res.send('URL ends with "fly"');
});
// 多个处理函数
app.get('/example',
(req, res, next) => {
console.log('First handler');
next();
},
(req, res) => {
res.send('Second handler');
}
);
3. 中间件
3.1 应用级中间件
// 全局中间件
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();
});
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
3.2 常用中间件
const express = require('express');
const morgan = require('morgan');
const cors = require('cors');
const helmet = require('helmet');
const compression = require('compression');
const app = express();
// 日志中间件
app.use(morgan('dev'));
// 安全中间件
app.use(helmet());
// CORS 中间件
app.use(cors());
// 压缩中间件
app.use(compression());
// 静态文件中间件
app.use(express.static('public'));
// 解析 JSON
app.use(express.json());
// 解析 URL-encoded bodies
app.use(express.urlencoded({ extended: true }));
4. 错误处理
4.1 同步错误处理
app.get('/sync-error', (req, res) => {
throw new Error('Sync Error');
});
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
message: err.message,
error: process.env.NODE_ENV === 'development' ? err : {}
});
});
4.2 异步错误处理
// 异步错误处理包装器
const asyncHandler = fn => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
app.get('/async-error', asyncHandler(async (req, res) => {
const result = await someAsyncOperation();
res.json(result);
}));
// 自定义错误类
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
5. 数据库集成
5.1 MongoDB (Mongoose)
const mongoose = require('mongoose');
// 连接数据库
mongoose.connect('mongodb://localhost/myapp', {
useNewUrlParser: true,
useUnifiedTopology: true
});
// 定义模型
const userSchema = new mongoose.Schema({
name: String,
email: { type: String, required: true, unique: true },
password: { type: String, required: true }
});
const User = mongoose.model('User', userSchema);
// 使用模型
app.post('/users', async (req, res) => {
try {
const user = new User(req.body);
await user.save();
res.status(201).json(user);
} catch (error) {
res.status(400).json({ error: error.message });
}
});
5.2 SQL (Sequelize)
const { Sequelize, DataTypes } = require('sequelize');
// 连接数据库
const sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: 'mysql'
});
// 定义模型
const User = sequelize.define('User', {
name: DataTypes.STRING,
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING,
allowNull: false
}
});
// 使用模型
app.post('/users', async (req, res) => {
try {
const user = await User.create(req.body);
res.status(201).json(user);
} catch (error) {
res.status(400).json({ error: error.message });
}
});
6. 认证和授权
6.1 JWT 认证
const jwt = require('jsonwebtoken');
// 认证中间件
const auth = (req, res, next) => {
try {
const token = req.header('Authorization').replace('Bearer ', '');
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
res.status(401).json({ error: 'Please authenticate' });
}
};
// 登录路由
app.post('/login', async (req, res) => {
try {
const user = await User.findOne({ email: req.body.email });
if (!user || !await user.comparePassword(req.body.password)) {
throw new Error('Invalid credentials');
}
const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET);
res.json({ token });
} catch (error) {
res.status(400).json({ error: error.message });
}
});
// 受保护的路由
app.get('/profile', auth, async (req, res) => {
const user = await User.findById(req.user.id);
res.json(user);
});
6.2 Session 认证
const session = require('express-session');
const MongoStore = require('connect-mongo');
// Session 配置
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
store: MongoStore.create({
mongoUrl: process.env.MONGODB_URI
}),
cookie: {
secure: process.env.NODE_ENV === 'production',
maxAge: 1000 * 60 * 60 * 24 // 24 hours
}
}));
// 登录路由
app.post('/login', async (req, res) => {
const user = await User.findOne({ email: req.body.email });
if (user && await user.comparePassword(req.body.password)) {
req.session.userId = user.id;
res.json({ message: 'Logged in successfully' });
} else {
res.status(401).json({ error: 'Invalid credentials' });
}
});
7. 测试
7.1 单元测试
// test/user.test.js
const request = require('supertest');
const app = require('../app');
const User = require('../models/user');
describe('User API', () => {
beforeEach(async () => {
await User.deleteMany();
});
test('Should create new user', async () => {
const response = await request(app)
.post('/users')
.send({
name: 'Test User',
email: 'test@example.com',
password: 'password123'
})
.expect(201);
const user = await User.findById(response.body._id);
expect(user).not.toBeNull();
});
});
7.2 集成测试
// test/auth.test.js
describe('Authentication', () => {
let token;
beforeEach(async () => {
const response = await request(app)
.post('/login')
.send({
email: 'test@example.com',
password: 'password123'
});
token = response.body.token;
});
test('Should get profile for authenticated user', async () => {
await request(app)
.get('/profile')
.set('Authorization', `Bearer ${token}`)
.expect(200);
});
});
8. 部署
8.1 生产环境配置
// config/production.js
module.exports = {
// 安全配置
app.use(helmet());
app.use(compression());
// 日志配置
if (process.env.NODE_ENV === 'production') {
app.use(morgan('combined'));
}
// 错误处理
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Server Error');
});
};