您现在的位置是:网站首页 > RESTful API开发支持文章详情
RESTful API开发支持
陈川
【
Node.js
】
8444人已围观
7481字
RESTful API是一种基于HTTP协议的架构风格,广泛应用于现代Web开发中。Express作为Node.js的轻量级框架,提供了简洁灵活的方式构建RESTful服务。下面从路由设计、状态码处理、数据验证等方面展开具体实现方法。
路由设计规范
RESTful API的核心在于资源导向的路由设计。Express中通过app.METHOD()
系列方法实现:
const express = require('express');
const app = express();
// 用户资源路由
app.get('/users', (req, res) => {
// 获取用户列表
});
app.post('/users', (req, res) => {
// 创建新用户
});
app.get('/users/:id', (req, res) => {
// 获取单个用户
});
app.put('/users/:id', (req, res) => {
// 全量更新用户
});
app.patch('/users/:id', (req, res) => {
// 部分更新用户
});
app.delete('/users/:id', (req, res) => {
// 删除用户
});
嵌套资源路由建议不超过两级:
// 文章评论嵌套路由
app.get('/articles/:articleId/comments', (req, res) => {
const { articleId } = req.params;
// 获取指定文章的所有评论
});
HTTP状态码应用
正确的状态码能显著提升API可读性。常见场景处理:
// 成功响应
res.status(200).json({ data: user });
res.status(201).json({ created: true });
// 客户端错误
res.status(400).json({ error: '参数校验失败' });
res.status(404).json({ error: '资源不存在' });
// 服务端错误
res.status(500).json({ error: '数据库连接失败' });
特殊状态码的应用示例:
// 204 No Content
app.delete('/posts/:id', (req, res) => {
deletePost(req.params.id);
res.status(204).end();
});
// 429 Too Many Requests
app.get('/api', (req, res) => {
if (rateLimitExceeded(req.ip)) {
res.status(429).json({
retryAfter: 60
});
}
});
请求数据验证
使用express-validator进行数据校验:
const { body, validationResult } = require('express-validator');
app.post('/users',
body('email').isEmail(),
body('password').isLength({ min: 8 }),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// 处理有效数据
}
);
复杂校验规则示例:
body('phone').custom(value => {
if (!/^1[3-9]\d{9}$/.test(value)) {
throw new Error('手机号格式错误');
}
return true;
}),
body('birthday').isISO8601().toDate()
响应数据格式化
统一响应结构增强客户端处理效率:
// 响应包装中间件
function responseWrapper(req, res, next) {
const originalJson = res.json;
res.json = function(data) {
originalJson.call(this, {
code: res.statusCode,
timestamp: new Date().toISOString(),
data: data
});
};
next();
}
// 使用示例
app.get('/products', responseWrapper, (req, res) => {
res.json(getAllProducts()); // 自动包装
});
分页响应示例:
app.get('/articles', (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const result = getPaginatedArticles(page, limit);
res.json({
items: result.data,
pagination: {
total: result.total,
page,
limit,
totalPages: Math.ceil(result.total / limit)
}
});
});
版本控制策略
API版本控制常见实现方式:
- URL路径版本控制:
app.get('/v1/users', v1UserController);
app.get('/v2/users', v2UserController);
- 请求头版本控制:
app.get('/users', (req, res) => {
const apiVersion = req.get('X-API-Version') || 'v1';
if (apiVersion === 'v2') {
return v2UserController(req, res);
}
return v1UserController(req, res);
});
- 查询参数版本控制:
app.get('/users', (req, res) => {
switch(req.query.version) {
case '2.0': return v2Controller(req, res);
default: return v1Controller(req, res);
}
});
认证授权实现
JWT认证完整示例:
const jwt = require('jsonwebtoken');
const expressJwt = require('express-jwt');
// 签发Token
app.post('/login', (req, res) => {
const user = authenticate(req.body);
const token = jwt.sign(
{ userId: user.id },
process.env.SECRET,
{ expiresIn: '2h' }
);
res.json({ token });
});
// 保护路由
app.get('/profile',
expressJwt({ secret: process.env.SECRET }),
(req, res) => {
res.json(getUserProfile(req.user.userId));
}
);
// 错误处理
app.use((err, req, res, next) => {
if (err.name === 'UnauthorizedError') {
res.status(401).json({ error: '无效的访问令牌' });
}
});
性能优化技巧
- 路由懒加载:
// 按需加载路由处理器
app.get('/reports', (req, res, next) => {
require('./handlers/reports')(req, res, next);
});
- 响应压缩:
const compression = require('compression');
app.use(compression());
- 缓存控制:
app.get('/static-data', (req, res) => {
res.set('Cache-Control', 'public, max-age=3600');
res.json(getStaticData());
});
错误处理机制
结构化错误处理中间件:
class APIError extends Error {
constructor(message, status = 500) {
super(message);
this.status = status;
}
}
// 业务代码中抛出错误
app.get('/orders/:id', (req, res) => {
const order = getOrder(req.params.id);
if (!order) throw new APIError('订单不存在', 404);
});
// 全局错误处理器
app.use((err, req, res, next) => {
const status = err.status || 500;
res.status(status).json({
error: {
message: err.message,
code: status,
stack: process.env.NODE_ENV === 'development' ? err.stack : undefined
}
});
});
文档自动生成
使用Swagger UI创建API文档:
const swaggerJsdoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
const options = {
definition: {
openapi: '3.0.0',
info: {
title: '电商API',
version: '1.0.0',
},
},
apis: ['./routes/*.js'], // 扫描路由文件
};
const specs = swaggerJsdoc(options);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));
路由注释示例:
/**
* @swagger
* /products:
* get:
* summary: 获取商品列表
* parameters:
* - in: query
* name: category
* schema:
* type: string
* responses:
* 200:
* description: 商品数组
*/
app.get('/products', productController.list);
测试策略
使用Jest进行API测试:
const request = require('supertest');
const app = require('../app');
describe('用户API测试', () => {
test('创建用户', async () => {
const res = await request(app)
.post('/users')
.send({
name: '测试用户',
email: 'test@example.com'
});
expect(res.statusCode).toEqual(201);
expect(res.body).toHaveProperty('id');
});
test('无效邮箱检测', async () => {
const res = await request(app)
.post('/users')
.send({ email: 'invalid' });
expect(res.statusCode).toEqual(400);
});
});
集成测试示例:
describe('订单流程测试', () => {
let authToken;
beforeAll(async () => {
const loginRes = await request(app)
.post('/login')
.send({ username: 'test', password: '123456' });
authToken = loginRes.body.token;
});
test('创建订单需要认证', async () => {
const res = await request(app)
.post('/orders')
.set('Authorization', `Bearer ${authToken}`)
.send({ productId: 1 });
expect(res.statusCode).toEqual(201);
});
});
部署注意事项
生产环境配置建议:
// 安全中间件
const helmet = require('helmet');
app.use(helmet());
// 请求限流
const rateLimit = require('express-rate-limit');
app.use('/api/', rateLimit({
windowMs: 15 * 60 * 1000,
max: 100
}));
// CORS配置
const cors = require('cors');
app.use(cors({
origin: ['https://example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE']
}));
日志记录方案:
const morgan = require('morgan');
const fs = require('fs');
const path = require('path');
// 访问日志
app.use(morgan('combined', {
stream: fs.createWriteStream(
path.join(__dirname, 'access.log'),
{ flags: 'a' }
)
}));
// 错误日志
app.use((err, req, res, next) => {
fs.appendFileSync('error.log', `${new Date().toISOString()} - ${err.stack}\n`);
next(err);
});
上一篇: 文件上传与下载处理
下一篇: WebSocket集成方案