Skip to main content

使用Node.js构建REST API(Express框架)

本教程将带你从零开始构建一个完整的RESTful API,使用Node.js和Express框架。你将学习如何创建路由、实现CRUD操作、处理错误、进行身份验证和测试。

什么是REST API?

REST(Representational State Transfer)是一种架构风格,用于设计网络应用程序。RESTful API遵循REST原则,使用HTTP方法(GET、POST、PUT、DELETE)来操作资源。

REST API的特点

  • 无状态:每个请求都包含处理该请求所需的所有信息
  • 统一接口:使用标准的HTTP方法
  • 资源导向:URL表示资源,HTTP方法表示操作
  • 可缓存:响应可以被缓存以提高性能

项目设置

1. 初始化项目

# 创建项目目录
mkdir nodejs-rest-api
cd nodejs-rest-api

# 初始化package.json
npm init -y

# 安装依赖
npm install express cors helmet morgan dotenv
npm install -D nodemon

2. 项目结构

nodejs-rest-api/
├── src/
│ ├── controllers/
│ │ └── userController.js
│ ├── middleware/
│ │ ├── auth.js
│ │ └── errorHandler.js
│ ├── models/
│ │ └── User.js
│ ├── routes/
│ │ └── userRoutes.js
│ ├── services/
│ │ └── userService.js
│ └── app.js
├── tests/
│ └── user.test.js
├── .env
├── .gitignore
└── package.json

基础Express应用

1. 创建主应用文件

// src/app.js
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
require('dotenv').config();

const app = express();
const PORT = process.env.PORT || 3000;

// 中间件
app.use(helmet()); // 安全头
app.use(cors()); // 跨域支持
app.use(morgan('combined')); // 日志记录
app.use(express.json()); // JSON解析
app.use(express.urlencoded({ extended: true })); // URL编码解析

// 路由
app.get('/', (req, res) => {
res.json({
message: 'Welcome to Node.js REST API',
version: '1.0.0',
endpoints: {
users: '/api/users',
health: '/api/health'
}
});
});

// 健康检查端点
app.get('/api/health', (req, res) => {
res.json({
status: 'OK',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});

// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
error: 'Something went wrong!',
message: process.env.NODE_ENV === 'development' ? err.message : 'Internal Server Error'
});
});

// 404处理
app.use('*', (req, res) => {
res.status(404).json({
error: 'Route not found',
message: `Cannot ${req.method} ${req.originalUrl}`
});
});

app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});

module.exports = app;

2. 环境配置

# .env
NODE_ENV=development
PORT=3000
JWT_SECRET=your-secret-key
DATABASE_URL=mongodb://localhost:27017/nodejs-api

用户管理API

1. 用户模型

// src/models/User.js
class User {
constructor(id, name, email, age) {
this.id = id;
this.name = name;
this.email = email;
this.age = age;
this.createdAt = new Date();
this.updatedAt = new Date();
}

// 验证用户数据
validate() {
const errors = [];

if (!this.name || this.name.trim().length < 2) {
errors.push('Name must be at least 2 characters long');
}

if (!this.email || !this.isValidEmail(this.email)) {
errors.push('Valid email is required');
}

if (!this.age || this.age < 0 || this.age > 150) {
errors.push('Age must be between 0 and 150');
}

return errors;
}

isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}

// 转换为JSON
toJSON() {
return {
id: this.id,
name: this.name,
email: this.email,
age: this.age,
createdAt: this.createdAt,
updatedAt: this.updatedAt
};
}
}

module.exports = User;

2. 用户服务

// src/services/userService.js
const User = require('../models/User');

class UserService {
constructor() {
this.users = [];
this.nextId = 1;
}

// 获取所有用户
getAllUsers() {
return this.users.map(user => user.toJSON());
}

// 根据ID获取用户
getUserById(id) {
const user = this.users.find(u => u.id === parseInt(id));
return user ? user.toJSON() : null;
}

// 创建新用户
createUser(userData) {
const user = new User(
this.nextId++,
userData.name,
userData.email,
userData.age
);

const errors = user.validate();
if (errors.length > 0) {
throw new Error(`Validation failed: ${errors.join(', ')}`);
}

this.users.push(user);
return user.toJSON();
}

// 更新用户
updateUser(id, userData) {
const userIndex = this.users.findIndex(u => u.id === parseInt(id));
if (userIndex === -1) {
return null;
}

const user = this.users[userIndex];

// 更新字段
if (userData.name) user.name = userData.name;
if (userData.email) user.email = userData.email;
if (userData.age !== undefined) user.age = userData.age;

user.updatedAt = new Date();

const errors = user.validate();
if (errors.length > 0) {
throw new Error(`Validation failed: ${errors.join(', ')}`);
}

return user.toJSON();
}

// 删除用户
deleteUser(id) {
const userIndex = this.users.findIndex(u => u.id === parseInt(id));
if (userIndex === -1) {
return false;
}

this.users.splice(userIndex, 1);
return true;
}

// 根据邮箱查找用户
getUserByEmail(email) {
const user = this.users.find(u => u.email === email);
return user ? user.toJSON() : null;
}
}

module.exports = new UserService();

3. 用户控制器

// src/controllers/userController.js
const userService = require('../services/userService');

class UserController {
// 获取所有用户
async getAllUsers(req, res) {
try {
const users = userService.getAllUsers();
res.json({
success: true,
data: users,
count: users.length
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
}

// 根据ID获取用户
async getUserById(req, res) {
try {
const { id } = req.params;
const user = userService.getUserById(id);

if (!user) {
return res.status(404).json({
success: false,
error: 'User not found'
});
}

res.json({
success: true,
data: user
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
}

// 创建新用户
async createUser(req, res) {
try {
const userData = req.body;
const user = userService.createUser(userData);

res.status(201).json({
success: true,
data: user,
message: 'User created successfully'
});
} catch (error) {
res.status(400).json({
success: false,
error: error.message
});
}
}

// 更新用户
async updateUser(req, res) {
try {
const { id } = req.params;
const userData = req.body;

const user = userService.updateUser(id, userData);

if (!user) {
return res.status(404).json({
success: false,
error: 'User not found'
});
}

res.json({
success: true,
data: user,
message: 'User updated successfully'
});
} catch (error) {
res.status(400).json({
success: false,
error: error.message
});
}
}

// 删除用户
async deleteUser(req, res) {
try {
const { id } = req.params;
const deleted = userService.deleteUser(id);

if (!deleted) {
return res.status(404).json({
success: false,
error: 'User not found'
});
}

res.json({
success: true,
message: 'User deleted successfully'
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
}
}

module.exports = new UserController();

4. 用户路由

// src/routes/userRoutes.js
const express = require('express');
const userController = require('../controllers/userController');
const authMiddleware = require('../middleware/auth');

const router = express.Router();

// 应用认证中间件到所有路由
router.use(authMiddleware);

// 用户路由
router.get('/', userController.getAllUsers);
router.get('/:id', userController.getUserById);
router.post('/', userController.createUser);
router.put('/:id', userController.updateUser);
router.delete('/:id', userController.deleteUser);

module.exports = router;

中间件

1. 认证中间件

// src/middleware/auth.js
const jwt = require('jsonwebtoken');

const authMiddleware = (req, res, next) => {
const token = req.header('Authorization')?.replace('Bearer ', '');

if (!token) {
return res.status(401).json({
success: false,
error: 'Access denied. No token provided.'
});
}

try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
res.status(400).json({
success: false,
error: 'Invalid token.'
});
}
};

module.exports = authMiddleware;

2. 错误处理中间件

// src/middleware/errorHandler.js
const errorHandler = (err, req, res, next) => {
console.error(err.stack);

// 验证错误
if (err.name === 'ValidationError') {
return res.status(400).json({
success: false,
error: 'Validation Error',
details: err.message
});
}

// JWT错误
if (err.name === 'JsonWebTokenError') {
return res.status(401).json({
success: false,
error: 'Invalid token'
});
}

// 默认错误
res.status(err.status || 500).json({
success: false,
error: process.env.NODE_ENV === 'development' ? err.message : 'Internal Server Error'
});
};

module.exports = errorHandler;

API测试

1. 使用curl测试

# 获取所有用户
curl -X GET http://localhost:3000/api/users \
-H "Authorization: Bearer your-jwt-token"

# 创建新用户
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-jwt-token" \
-d '{
"name": "John Doe",
"email": "[email protected]",
"age": 30
}'

# 更新用户
curl -X PUT http://localhost:3000/api/users/1 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-jwt-token" \
-d '{
"name": "John Smith",
"age": 31
}'

# 删除用户
curl -X DELETE http://localhost:3000/api/users/1 \
-H "Authorization: Bearer your-jwt-token"

2. 使用Postman测试

  1. 创建新的Collection
  2. 设置环境变量(base_url, token)
  3. 创建请求并测试各个端点

最佳实践

1. 错误处理

// 统一错误响应格式
const sendErrorResponse = (res, statusCode, message, details = null) => {
res.status(statusCode).json({
success: false,
error: message,
...(details && { details })
});
};

2. 输入验证

// 使用Joi进行输入验证
const Joi = require('joi');

const userSchema = Joi.object({
name: Joi.string().min(2).max(50).required(),
email: Joi.string().email().required(),
age: Joi.number().integer().min(0).max(150).required()
});

3. 日志记录

// 使用Winston进行日志记录
const winston = require('winston');

const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});

部署

1. 生产环境配置

// 生产环境优化
if (process.env.NODE_ENV === 'production') {
app.use(compression());
app.use(express.static('public'));
}

2. Docker部署

# Dockerfile
FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

EXPOSE 3000

CMD ["node", "src/app.js"]

总结

本教程涵盖了使用Node.js和Express构建REST API的核心概念:

  • 项目结构:组织代码的最佳实践
  • 路由设计:RESTful API设计原则
  • 中间件:认证、错误处理、日志记录
  • 数据验证:输入验证和错误处理
  • 测试:API端点的测试方法
  • 部署:生产环境部署策略

通过这个教程,你已经掌握了构建生产级REST API的基础知识。继续学习数据库集成、身份验证和性能优化等高级主题。

进一步学习


本教程是Node.js系列的一部分,由syscook.dev提供