跳到主要内容

API设计

介绍

API(应用程序编程接口)设计是后端开发的核心环节,它定义了应用程序之间交互的规则和方式。良好的API设计能够提高开发效率、降低集成成本、提升用户体验,并确保系统的可扩展性和可维护性。随着微服务架构的普及,API设计的重要性更加凸显,它已成为服务之间通信的关键桥梁。

原理

API设计的核心原理:

  • RESTful原则:基于资源的设计,使用标准HTTP方法
  • 清晰性:接口名称和功能清晰明了,易于理解和使用
  • 一致性:保持接口风格、参数命名和返回格式的一致性
  • 版本控制:支持API版本演进,确保向后兼容
  • 安全性:实现身份认证、授权和数据加密
  • 性能:优化接口响应时间,减少不必要的网络传输
  • 可扩展性:设计支持未来功能扩展的接口
  • 错误处理:提供明确的错误信息和错误码

图示

RESTful API设计
资源: /users, /products, /orders
HTTP方法: GET(获取), POST(创建), PUT(更新), DELETE(删除)
状态码: 200(成功), 201(创建成功), 400(请求错误), 401(未认证), 403(禁止访问), 404(资源不存在), 500(服务器错误)

API设计流程
需求分析 → 资源识别 → 端点设计 → 参数设计 → 响应设计 → 错误处理 → 安全设计 → 文档编写

API版本控制策略
1. URL路径版本: /api/v1/users
2. 查询参数版本: /api/users?version=1
3. 请求头版本: Accept-Version: 1.0

实例

RESTful API设计示例

const express = require('express');
const app = express();
app.use(express.json());

// 用户API
// 获取所有用户
app.get('/api/v1/users', authenticateJWT, async (req, res) => {
const { page = 1, limit = 10 } = req.query;
const skip = (page - 1) * limit;

const users = await User
.find({}, { name: 1, email: 1, _id: 1 }) // 投影
.sort({ createdAt: -1 })
.skip(skip)
.limit(Number(limit))
.lean();

const total = await User.countDocuments();

res.json({
data: users,
meta: {
total,
totalPages: Math.ceil(total / limit),
currentPage: page
}
});
});

// 获取单个用户
app.get('/api/v1/users/:id', authenticateJWT, async (req, res) => {
try {
const user = await User.findById(req.params.id).lean();
if (!user) {
return res.status(404).json({
error: {
code: 'USER_NOT_FOUND',
message: '用户不存在'
}
});
}
res.json({ data: user });
} catch (error) {
res.status(400).json({
error: {
code: 'INVALID_USER_ID',
message: '无效的用户ID'
}
});
}
});

// 创建用户
app.post('/api/v1/users', authenticateJWT, isAdmin, async (req, res) => {
try {
// 输入验证
const { error } = validateUser(req.body);
if (error) {
return res.status(400).json({
error: {
code: 'VALIDATION_ERROR',
message: error.details[0].message
}
});
}

// 检查邮箱是否已存在
const existingUser = await User.findOne({ email: req.body.email });
if (existingUser) {
return res.status(409).json({
error: {
code: 'EMAIL_ALREADY_EXISTS',
message: '邮箱已被注册'
}
});
}

// 密码加密
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(req.body.password, salt);

// 创建用户
const user = new User({
name: req.body.name,
email: req.body.email,
password: hashedPassword,
role: req.body.role || 'user'
});

await user.save();

// 返回创建的用户(不包含密码)
const userResponse = { ...user.toObject() };
delete userResponse.password;

res.status(201).json({
data: userResponse,
message: '用户创建成功'
});
} catch (error) {
res.status(500).json({
error: {
code: 'SERVER_ERROR',
message: '服务器内部错误'
}
});
}
});

// 更新用户
app.put('/api/v1/users/:id', authenticateJWT, async (req, res) => {
try {
// 权限检查
if (req.user.role !== 'admin' && req.user.userId !== req.params.id) {
return res.status(403).json({
error: {
code: 'FORBIDDEN',
message: '无权执行此操作'
}
});
}

// 输入验证
const { error } = validateUserUpdate(req.body);
if (error) {
return res.status(400).json({
error: {
code: 'VALIDATION_ERROR',
message: error.details[0].message
}
});
}

// 准备更新数据
const updateData = { ...req.body };

// 如果更新密码,需要加密
if (updateData.password) {
const salt = await bcrypt.genSalt(10);
updateData.password = await bcrypt.hash(updateData.password, salt);
}

// 更新用户
const user = await User.findByIdAndUpdate(
req.params.id,
updateData,
{ new: true, runValidators: true }
).lean();

if (!user) {
return res.status(404).json({
error: {
code: 'USER_NOT_FOUND',
message: '用户不存在'
}
});
}

// 返回更新后的用户(不包含密码)
const userResponse = { ...user };
delete userResponse.password;

res.json({
data: userResponse,
message: '用户更新成功'
});
} catch (error) {
if (error.name === 'CastError') {
return res.status(400).json({
error: {
code: 'INVALID_USER_ID',
message: '无效的用户ID'
}
});
}
res.status(500).json({
error: {
code: 'SERVER_ERROR',
message: '服务器内部错误'
}
});
}
});

// 删除用户
app.delete('/api/v1/users/:id', authenticateJWT, isAdmin, async (req, res) => {
try {
const user = await User.findByIdAndDelete(req.params.id);
if (!user) {
return res.status(404).json({
error: {
code: 'USER_NOT_FOUND',
message: '用户不存在'
}
});
}
res.json({
message: '用户删除成功'
});
} catch (error) {
if (error.name === 'CastError') {
return res.status(400).json({
error: {
code: 'INVALID_USER_ID',
message: '无效的用户ID'
}
});
}
res.status(500).json({
error: {
code: 'SERVER_ERROR',
message: '服务器内部错误'
}
});
}
});

GraphQL API设计示例

const { ApolloServer, gql } = require('apollo-server-express');
const express = require('express');
const app = express();

// 定义模式
const typeDefs = gql`
type User {
id: ID!
name: String!
email: String!
role: String!
createdAt: String!
}

type Product {
id: ID!
name: String!
description: String
price: Float!
category: Category
inventory: Int!
}

type Category {
id: ID!
name: String!
products: [Product!]!
}

type Query {
users(page: Int = 1, limit: Int = 10): [User!]!
user(id: ID!): User
products(page: Int = 1, limit: Int = 10): [Product!]!
product(id: ID!): Product
categories: [Category!]!
}

type Mutation {
createUser(name: String!, email: String!, password: String!, role: String = "user"): User!
updateUser(id: ID!, name: String, email: String, password: String, role: String): User!
deleteUser(id: ID!): Boolean!
}
`;

// 解析器
const resolvers = {
Query: {
users: async (parent, { page, limit }) => {
const skip = (page - 1) * limit;
return await User.find({}).skip(skip).limit(limit).lean();
},
user: async (parent, { id }) => {
return await User.findById(id).lean();
},
products: async (parent, { page, limit }) => {
const skip = (page - 1) * limit;
return await Product.find({}).skip(skip).limit(limit).lean();
},
product: async (parent, { id }) => {
return await Product.findById(id).lean();
},
categories: async () => {
return await Category.find({}).lean();
}
},
Mutation: {
createUser: async (parent, { name, email, password, role }) => {
// 密码加密
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);

const user = new User({
name,
email,
password: hashedPassword,
role
});

await user.save();
return user.toObject();
},
updateUser: async (parent, { id, name, email, password, role }) => {
const updateData = {};
if (name) updateData.name = name;
if (email) updateData.email = email;
if (password) {
const salt = await bcrypt.genSalt(10);
updateData.password = await bcrypt.hash(password, salt);
}
if (role) updateData.role = role;

const user = await User.findByIdAndUpdate(id, updateData, { new: true }).lean();
if (!user) throw new Error('用户不存在');
return user;
},
deleteUser: async (parent, { id }) => {
const result = await User.findByIdAndDelete(id);
return !!result;
}
},
Category: {
products: async (parent) => {
return await Product.find({ category: parent.id }).lean();
}
},
Product: {
category: async (parent) => {
return await Category.findById(parent.category).lean();
}
}
};

// 创建Apollo服务器
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => {
// 认证逻辑
const token = req.headers.authorization || '';
if (token) {
try {
const user = jwt.verify(token.split(' ')[1], process.env.JWT_SECRET);
return { user };
} catch (error) {
// 无效令牌,忽略
}
}
return {};
}
});

// 应用中间件
server.applyMiddleware({ app });

app.listen({ port: 4000 }, () => {
console.log(`GraphQL服务器运行在 http://localhost:4000${server.graphqlPath}`);
});

专业解决方案

API设计风格

  • RESTful API:基于资源的设计,使用标准HTTP方法和状态码
  • GraphQL:由客户端指定所需数据,减少网络传输
  • gRPC:基于Protocol Buffers的高性能RPC框架,适合服务间通信
  • WebSocket:实现双向实时通信,适合聊天、游戏等场景
  • SOAP:基于XML的协议,适合企业级应用集成

API版本控制

  • URL路径版本:/api/v1/users,清晰明了,易于理解
  • 查询参数版本:/api/users?version=1,适合频繁更新的API
  • 请求头版本:Accept-Version: 1.0,URL更干净,但实现复杂
  • 媒体类型版本:Accept: application/vnd.example.v1+json,符合HTTP标准

请求与响应设计

  • 请求格式:使用JSON作为请求体格式
  • 响应格式:统一的响应结构,包含data、meta和error字段
  • 分页设计:使用page、limit参数,返回总记录数和总页数
  • 排序设计:使用sort参数,如sort=createdAt:desc
  • 过滤设计:使用查询参数过滤,如status=active&category=electronics
  • 字段选择:允许客户端选择需要的字段,减少数据传输

错误处理

  • 统一错误格式:包含错误码、错误消息和详细信息
  • HTTP状态码:使用合适的HTTP状态码表示错误类型
  • 错误分类:业务错误、系统错误、验证错误等
  • 错误日志:记录详细错误信息,便于调试和追踪
  • 避免暴露敏感信息:不在错误信息中暴露系统细节

API安全

  • 身份认证:JWT、OAuth 2.0、API密钥
  • 授权:RBAC、ABAC、权限检查
  • 输入验证:防止注入攻击、XSS等
  • 限流:防止API滥用,保护系统资源
  • CORS配置:限制跨域请求
  • HTTPS:加密传输数据
  • 防重放攻击:使用nonce和timestamp

API文档

  • Swagger/OpenAPI:自动生成和维护API文档
  • GraphQL Playground:交互式GraphQL API文档
  • API Blueprint:基于Markdown的API文档格式
  • RAML:RESTful API建模语言
  • 示例代码:提供各种语言的调用示例
  • 变更日志:记录API版本变更内容

API测试

  • 单元测试:测试单个API端点的功能
  • 集成测试:测试API之间的交互
  • 契约测试:确保API符合约定的契约
  • 性能测试:测试API的响应时间和吞吐量
  • 安全测试:测试API的安全性,发现漏洞

工具推荐

  • API框架:Express.js、Koa.js、NestJS、Fastify、Django REST framework
  • GraphQL工具:Apollo Server、GraphQL Yoga、Prisma
  • API文档:Swagger UI、ReDoc、GraphQL Playground
  • API测试:Supertest、Jest、Mocha、Chai、Cypress
  • API监控:New Relic、Datadog、Prometheus + Grafana
  • API网关:Kong、APISIX、Nginx、AWS API Gateway