Skip to main content

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');
});
};

8.2 PM2 部署

// ecosystem.config.js
module.exports = {
apps: [{
name: 'express-app',
script: './app.js',
instances: 'max',
exec_mode: 'cluster',
env_production: {
NODE_ENV: 'production',
PORT: 80
}
}]
};

参考资源