RESTful API接口设计规范与最佳实践:从入门到精通

RESTful API接口设计规范封面 - HTTP方法与URL设计最佳实践

引言

上周帮一个朋友Code Review代码,他写了一个用户管理接口:

plaintext

GET /getUserById?id=1
POST /createUser
POST /updateUser
GET /deleteUser?id=1

我看完差点没背过气去。这接口设计…怎么说呢,能用,但总觉得哪里不对劲。

后来我问他为什么这么设计,他说:”我看很多老项目都是这么写的啊!”

好吧,这篇文章就是来解决这个问题的。我会把我这些年设计API踩过的坑、总结的经验全部写下来,希望能帮你设计出更规范的接口。

RESTful API设计示例配图 - 接口规范与状态码使用图示

REST是什么

在说RESTful API之前,先得搞清楚REST是什么。

REST(Representational State Transfer)是Roy Fielding在2000年提出的架构风格,不是标准,也不是协议。它描述了一种设计Web服务的思路。

简单说,REST的核心思想是:用URL定位资源,用HTTP动词描述操作。

比如:

操作RESTful写法非RESTful写法
获取用户列表GET /usersGET /getUsers
获取单个用户GET /users/1GET /getUserById?id=1
创建用户POST /usersPOST /createUser
更新用户PUT /users/1POST /updateUser
删除用户DELETE /users/1GET /deleteUser?id=1

看到了吗?RESTful的核心就是把所有的”动作”都用HTTP方法来表示,而不是在URL里写动词

URL设计规范

URL(Uniform Resource Locator)是API的门面,设计得好不好直接影响开发者体验。

基本原则

  1. 用名词表示资源,不用动词

bash

# 好
GET /users
GET /users/123
GET /articles/456/comments

# 不好
GET /getUsers
GET /getUser
GET /queryUsersById
  1. 用复数名词表示集合

bash

# 好
GET /users
POST /users

# 不好
GET /user
POST /userList
  1. 用小写字母,用连字符分隔单词

bash

# 好
GET /user-profiles
GET /blog-posts

# 不推荐(虽然也能工作)
GET /userProfiles
GET /blog_posts  # 下划线在某些字体里容易看不清
  1. 层级关系用斜杠表示

bash

# 获取用户123的所有文章
GET /users/123/articles

# 获取用户123的文章456的评论
GET /users/123/articles/456/comments

查询参数的使用

当需要对资源进行过滤、排序、分页时,使用查询参数:

bash

# 过滤
GET /users?status=active&role=admin

# 排序
GET /users?sort=created_at&order=desc

# 分页
GET /users?page=2&page_size=20

# 搜索
GET /users?search=张三

避免过深的层级

虽然REST支持多层嵌套,但不要太深:

bash

# 不推荐(嵌套太深)
GET /organizations/123/departments/456/teams/789/members/101

# 推荐(扁平化设计)
GET /members/101?organization=123&department=456&team=789

HTTP方法:正确使用动词

HTTP定义了一组方法(也叫动词),RESTful API要用这些方法来表示操作。

常用方法

方法用途幂等性安全性
GET获取资源
POST创建资源
PUT完整更新资源
PATCH部分更新资源
DELETE删除资源

幂等性:同样的请求执行一次和执行多次,效果是一样的。
安全性:不会改变服务器状态。

GET:获取资源

bash

# 获取所有用户(列表)
GET /users

# 获取单个用户
GET /users/123

# 获取用户的好友列表
GET /users/123/friends

# 获取多个特定用户
GET /users?id=1&id=2&id=3

POST:创建资源

bash

# 创建用户
POST /users
Content-Type: application/json

{
    "name": "张三",
    "email": "zhangsan@example.com",
    "password": "123456"
}

响应:

http

HTTP/1.1 201 Created
Location: /users/456
Content-Type: application/json

{
    "id": 456,
    "name": "张三",
    "email": "zhangsan@example.com",
    "created_at": "2026-04-15T10:30:00Z"
}

注意返回状态码201和Location头。

PUT:完整更新资源

PUT要求提交完整的资源数据,缺少的字段会被设为默认值或清空:

bash

PUT /users/456
Content-Type: application/json

{
    "name": "张三改",
    "email": "zhangsan_changed@example.com",
    "password": "654321",
    "status": "active"
}

PATCH:部分更新资源

PATCH只更新提供的字段,其他字段保持不变:

bash

PATCH /users/456
Content-Type: application/json

{
    "email": "zhangsan_new@example.com"
}

DELETE:删除资源

bash

DELETE /users/456

成功删除返回204 No Content:

http

HTTP/1.1 204 No Content

状态码:让客户端知道发生了什么

状态码是HTTP响应的核心部分,它告诉客户端请求的结果是什么。

常用状态码

状态码含义使用场景
200 OK成功GET、PUT、PATCH成功
201 Created创建成功POST创建新资源成功
204 No Content无内容DELETE成功,响应无body
400 Bad Request请求错误参数校验失败、格式错误
401 Unauthorized未认证需要登录
403 Forbidden无权限已登录但无权限
404 Not Found资源不存在找不到指定资源
409 Conflict冲突资源冲突,如重复创建
422 Unprocessable Entity验证失败数据验证失败
500 Internal Server Error服务器错误程序异常
503 Service Unavailable服务不可用维护或过载

分层状态码

不要只用200和500,要根据情况返回合适的状态码:

javascript

// Express.js 示例
app.get('/users/:id', async (req, res) => {
    try {
        const userId = parseInt(req.params.id);
        
        if (isNaN(userId)) {
            return res.status(400).json({
                error: 'Invalid user ID',
                message: 'User ID must be a number'
            });
        }
        
        const user = await db.getUser(userId);
        
        if (!user) {
            return res.status(404).json({
                error: 'User not found',
                message: `No user with ID ${userId}`
            });
        }
        
        res.json(user);
    } catch (error) {
        console.error('Database error:', error);
        res.status(500).json({
            error: 'Internal server error',
            message: 'Please try again later'
        });
    }
});

请求和响应格式

JSON是你的好朋友

现代API基本都用JSON作为数据格式:

http

POST /users
Content-Type: application/json
Accept: application/json

{
    "name": "张三",
    "email": "zhangsan@example.com"
}

响应:

http

HTTP/1.1 201 Created
Content-Type: application/json

{
    "id": 123,
    "name": "张三",
    "email": "zhangsan@example.com",
    "created_at": "2026-04-15T10:30:00Z"
}

统一的响应结构

建议所有API都用统一的响应格式:

javascript

// 成功响应
{
    "success": true,
    "data": { ... },
    "message": "操作成功"
}

// 分页响应
{
    "success": true,
    "data": {
        "items": [...],
        "pagination": {
            "page": 1,
            "page_size": 20,
            "total": 100,
            "total_pages": 5
        }
    }
}

// 错误响应
{
    "success": false,
    "error": {
        "code": "VALIDATION_ERROR",
        "message": "参数校验失败",
        "details": [
            { "field": "email", "message": "邮箱格式不正确" },
            { "field": "password", "message": "密码长度不能少于6位" }
        ]
    }
}

时间格式

统一使用ISO 8601格式:

json

{
    "created_at": "2026-04-15T10:30:00Z",
    "updated_at": "2026-04-15T12:45:30+08:00"
}

字段命名

统一用驼峰或蛇形,两者选其一:

json

// 驼峰(推荐,JavaScript风格)
{
    "userId": 123,
    "userName": "张三",
    "createdAt": "2026-04-15T10:30:00Z"
}

// 蛇形(Python风格)
{
    "user_id": 123,
    "user_name": "张三",
    "created_at": "2026-04-15T10:30:00Z"
}

认证与授权

认证(Authentication)

确认”你是谁”,常用方式:

1. API Key

bash

GET /users?api_key=your_api_key_here

适合服务端之间的调用,不适合用户认证。

2. Bearer Token(JWT)

bash

GET /users
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

最常用的方式,适合移动端和前端。

3. OAuth 2.0

适合第三方登录,如微信登录、Google登录等。

授权(Authorization)

确认”你能做什么”,常用方式:

javascript

// 简单角色检查
function authorize(roles = []) {
    return (req, res, next) => {
        if (!req.user) {
            return res.status(401).json({ error: 'Unauthorized' });
        }
        
        if (!roles.includes(req.user.role)) {
            return res.status(403).json({ error: 'Forbidden' });
        }
        
        next();
    };
}

// 使用中间件
app.delete('/users/:id', 
    authenticate,  // 确认登录
    authorize(['admin']),  // 确认是管理员
    async (req, res) => {
        // 删除用户逻辑
    }
);

分页、排序和过滤

分页

bash

# 页码分页
GET /users?page=2&page_size=20

# 偏移分页(适合大数据量)
GET /users?offset=40&limit=20

# 光标分页(适合实时数据)
GET /users?cursor=abc123&limit=20

响应示例:

json

{
    "data": [...],
    "pagination": {
        "page": 2,
        "page_size": 20,
        "total": 100,
        "total_pages": 5,
        "has_next": true,
        "has_prev": true
    }
}

排序

bash

# 按创建时间降序
GET /users?sort=created_at&order=desc

# 多字段排序
GET /users?sort=role,created_at&order=asc,desc

过滤

bash

# 单个条件
GET /users?status=active

# 多个条件
GET /users?status=active&role=admin

# 范围查询
GET /users?age_gte=18&age_lte=60

# 模糊搜索
GET /users?search=张三

错误处理

错误响应格式

json

{
    "success": false,
    "error": {
        "code": "RESOURCE_NOT_FOUND",
        "message": "请求的资源不存在",
        "details": {
            "resource": "user",
            "id": "12345"
        }
    },
    "request_id": "req_abc123"
}

错误码设计

建议使用枚举定义错误码:

javascript

const ErrorCodes = {
    // 通用错误(1000-1999)
    INTERNAL_ERROR: { code: 1000, status: 500, message: '服务器内部错误' },
    INVALID_PARAMETER: { code: 1001, status: 400, message: '参数错误' },
    
    // 认证错误(2000-2999)
    UNAUTHORIZED: { code: 2000, status: 401, message: '未认证' },
    TOKEN_EXPIRED: { code: 2001, status: 401, message: 'Token已过期' },
    
    // 权限错误(3000-3999)
    FORBIDDEN: { code: 3000, status: 403, message: '无权限访问' },
    
    // 资源错误(4000-4999)
    NOT_FOUND: { code: 4000, status: 404, message: '资源不存在' },
    ALREADY_EXISTS: { code: 4001, status: 409, message: '资源已存在' },
    
    // 业务错误(5000-5999)
    INSUFFICIENT_BALANCE: { code: 5000, status: 400, message: '余额不足' }
};

// 统一错误处理函数
function handleError(errorCode, details = {}) {
    return {
        success: false,
        error: {
            code: errorCode.code,
            message: errorCode.message,
            ...details
        }
    };
}

// 使用
app.get('/users/:id', async (req, res) => {
    const user = await getUser(req.params.id);
    if (!user) {
        return res.status(404).json(handleError(ErrorCodes.NOT_FOUND, { resource: 'user' }));
    }
    res.json(user);
});

API版本管理

当API需要升级但不兼容旧版本时,需要版本管理。

URL路径版本(推荐)

bash

GET /v1/users
GET /v2/users

优点:直观,易于调试
缺点:代码需要维护多个版本

Header版本

bash

GET /users
Accept: application/vnd.example.v2+json

优点:URL保持干净
缺点:调试不方便

版本共存策略

javascript

// Express.js 示例
const v1Routes = require('./routes/v1');
const v2Routes = require('./routes/v2');

app.use('/v1', v1Routes);
app.use('/v2', v2Routes);

废弃旧版本

http

GET /v1/users

# 响应头提示
Deprecation: true
Sunset: Thu, 31 Dec 2026 23:59:59 GMT
Link: <https://api.example.com/v2/users>; rel="successor-version"

实战:完整的用户管理API

项目结构

plaintext

user-api/
├── src/
│   ├── routes/
│   │   └── users.js
│   ├── middleware/
│   │   ├── auth.js
│   │   └── errorHandler.js
│   ├── models/
│   │   └── User.js
│   ├── controllers/
│   │   └── userController.js
│   ├── utils/
│   │   └── errors.js
│   └── app.js
├── package.json
└── README.md

Express.js实现

src/utils/errors.js:错误定义

javascript

class AppError extends Error {
    constructor(code, statusCode, message) {
        super(message);
        this.code = code;
        this.statusCode = statusCode;
        this.isOperational = true;
        
        Error.captureStackTrace(this, this.constructor);
    }
}

class ValidationError extends AppError {
    constructor(message, details = []) {
        super('VALIDATION_ERROR', 400, message);
        this.details = details;
    }
}

class NotFoundError extends AppError {
    constructor(resource, id) {
        super('NOT_FOUND', 404, `${resource} with id ${id} not found`);
        this.resource = resource;
        this.resourceId = id;
    }
}

class UnauthorizedError extends AppError {
    constructor(message = 'Authentication required') {
        super('UNAUTHORIZED', 401, message);
    }
}

class ForbiddenError extends AppError {
    constructor(message = 'Access denied') {
        super('FORBIDDEN', 403, message);
    }
}

module.exports = {
    AppError,
    ValidationError,
    NotFoundError,
    UnauthorizedError,
    ForbiddenError
};

src/middleware/errorHandler.js:错误处理中间件

javascript

const { AppError } = require('../utils/errors');

function errorHandler(err, req, res, next) {
    console.error('Error:', err);
    
    if (err instanceof AppError) {
        return res.status(err.statusCode).json({
            success: false,
            error: {
                code: err.code,
                message: err.message,
                details: err.details || undefined
            },
            request_id: req.id
        });
    }
    
    // 开发环境返回详细错误
    if (process.env.NODE_ENV === 'development') {
        return res.status(500).json({
            success: false,
            error: {
                code: 'INTERNAL_ERROR',
                message: err.message,
                stack: err.stack
            }
        });
    }
    
    // 生产环境返回通用错误
    res.status(500).json({
        success: false,
        error: {
            code: 'INTERNAL_ERROR',
            message: 'An unexpected error occurred'
        }
    });
}

module.exports = errorHandler;

src/middleware/auth.js:认证中间件

javascript

const jwt = require('jsonwebtoken');
const { UnauthorizedError } = require('../utils/errors');

function authenticate(req, res, next) {
    const authHeader = req.headers.authorization;
    
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
        throw new UnauthorizedError('No token provided');
    }
    
    const token = authHeader.substring(7);
    
    try {
        const decoded = jwt.verify(token, process.env.JWT_SECRET);
        req.user = decoded;
        next();
    } catch (error) {
        if (error.name === 'TokenExpiredError') {
            throw new UnauthorizedError('Token expired');
        }
        throw new UnauthorizedError('Invalid token');
    }
}

function authorize(...roles) {
    return (req, res, next) => {
        if (!req.user) {
            throw new UnauthorizedError();
        }
        
        if (roles.length > 0 && !roles.includes(req.user.role)) {
            throw new ForbiddenError();
        }
        
        next();
    };
}

module.exports = { authenticate, authorize };

src/models/User.js:数据模型(模拟)

javascript

// 模拟数据库
const users = new Map();
let nextId = 1;

class User {
    static findAll(filters = {}) {
        let result = Array.from(users.values());
        
        // 应用过滤条件
        if (filters.status) {
            result = result.filter(u => u.status === filters.status);
        }
        if (filters.role) {
            result = result.filter(u => u.role === filters.role);
        }
        if (filters.search) {
            const search = filters.search.toLowerCase();
            result = result.filter(u => 
                u.name.toLowerCase().includes(search) ||
                u.email.toLowerCase().includes(search)
            );
        }
        
        // 排序
        if (filters.sort) {
            const order = filters.order === 'desc' ? -1 : 1;
            result.sort((a, b) => {
                if (a[filters.sort] < b[filters.sort]) return -1 * order;
                if (a[filters.sort] > b[filters.sort]) return 1 * order;
                return 0;
            });
        }
        
        return result;
    }
    
    static findById(id) {
        return users.get(id);
    }
    
    static create(data) {
        const id = nextId++;
        const user = {
            id,
            ...data,
            status: 'active',
            created_at: new Date().toISOString(),
            updated_at: new Date().toISOString()
        };
        users.set(id, user);
        return user;
    }
    
    static update(id, data) {
        const user = users.get(id);
        if (!user) return null;
        
        const updated = {
            ...user,
            ...data,
            id,  // 确保id不可修改
            updated_at: new Date().toISOString()
        };
        users.set(id, updated);
        return updated;
    }
    
    static delete(id) {
        return users.delete(id);
    }
}

module.exports = User;

src/controllers/userController.js:控制器

javascript

const User = require('../models/User');
const { ValidationError, NotFoundError } = require('../utils/errors');

// 获取用户列表
async function getUsers(req, res) {
    const filters = {
        status: req.query.status,
        role: req.query.role,
        search: req.query.search,
        sort: req.query.sort || 'created_at',
        order: req.query.order || 'desc'
    };
    
    const page = parseInt(req.query.page) || 1;
    const pageSize = Math.min(parseInt(req.query.page_size) || 20, 100);
    
    const allUsers = User.findAll(filters);
    const total = allUsers.length;
    const totalPages = Math.ceil(total / pageSize);
    
    const start = (page - 1) * pageSize;
    const users = allUsers.slice(start, start + pageSize);
    
    res.json({
        success: true,
        data: {
            items: users,
            pagination: {
                page,
                page_size: pageSize,
                total,
                total_pages: totalPages,
                has_next: page < totalPages,
                has_prev: page > 1
            }
        }
    });
}

// 获取单个用户
async function getUser(req, res) {
    const user = User.findById(parseInt(req.params.id));
    
    if (!user) {
        throw new NotFoundError('user', req.params.id);
    }
    
    res.json({
        success: true,
        data: user
    });
}

// 创建用户
async function createUser(req, res) {
    const { name, email, password, role } = req.body;
    
    // 验证
    if (!name || !email || !password) {
        throw new ValidationError('Missing required fields', [
            { field: 'name', message: 'Name is required' },
            { field: 'email', message: 'Email is required' },
            { field: 'password', message: 'Password is required' }
        ]);
    }
    
    // 简单邮箱格式验证
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(email)) {
        throw new ValidationError('Invalid email format');
    }
    
    // 密码长度验证
    if (password.length < 6) {
        throw new ValidationError('Password too short');
    }
    
    const user = User.create({ name, email, password, role });
    
    res.status(201).json({
        success: true,
        data: user
    });
}

// 更新用户
async function updateUser(req, res) {
    const id = parseInt(req.params.id);
    
    // 检查用户是否存在
    const existingUser = User.findById(id);
    if (!existingUser) {
        throw new NotFoundError('user', id);
    }
    
    // 邮箱格式验证(如果提供了email)
    if (req.body.email) {
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        if (!emailRegex.test(req.body.email)) {
            throw new ValidationError('Invalid email format');
        }
    }
    
    const user = User.update(id, req.body);
    
    res.json({
        success: true,
        data: user
    });
}

// 删除用户
async function deleteUser(req, res) {
    const id = parseInt(req.params.id);
    
    const deleted = User.delete(id);
    if (!deleted) {
        throw new NotFoundError('user', id);
    }
    
    res.status(204).send();
}

module.exports = {
    getUsers,
    getUser,
    createUser,
    updateUser,
    deleteUser
};

src/routes/users.js:路由

javascript

const express = require('express');
const router = express.Router();
const { authenticate, authorize } = require('../middleware/auth');
const userController = require('../controllers/userController');

// 公开路由
router.get('/', userController.getUsers);
router.get('/:id', userController.getUser);

// 需要认证的路由
router.post('/', authenticate, authorize('admin'), userController.createUser);
router.put('/:id', authenticate, authorize('admin'), userController.updateUser);
router.delete('/:id', authenticate, authorize('admin'), userController.deleteUser);

module.exports = router;

src/app.js:主应用

javascript

const express = require('express');
const errorHandler = require('./middleware/errorHandler');
const usersRouter = require('./routes/users');

const app = express();

// 中间件
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// 请求日志(简单版)
app.use((req, res, next) => {
    console.log(`${new Date().toISOString()} ${req.method} ${req.url}`);
    next();
});

// 路由
app.use('/api/users', usersRouter);

// 健康检查
app.get('/health', (req, res) => {
    res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

// 404处理
app.use((req, res) => {
    res.status(404).json({
        success: false,
        error: {
            code: 'NOT_FOUND',
            message: `Route ${req.method} ${req.url} not found`
        }
    });
});

// 错误处理
app.use(errorHandler);

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

module.exports = app;

运行测试

bash

# 安装依赖
npm install express jsonwebtoken

# 启动服务
node src/app.js

测试接口:

bash

# 健康检查
curl http://localhost:3000/health

# 获取用户列表
curl http://localhost:3000/api/users

# 创建用户(需要token,这里简化处理)
curl -X POST http://localhost:3000/api/users \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_token_here" \
  -d '{"name":"张三","email":"zhangsan@example.com","password":"123456"}'

# 获取单个用户
curl http://localhost:3000/api/users/1

# 更新用户
curl -X PUT http://localhost:3000/api/users/1 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your_token_here" \
  -d '{"name":"张三改"}'

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

文档与调试

API文档

好的文档比代码还重要。推荐使用OpenAPI(Swagger)规范:

yaml

# openapi.yaml
openapi: 3.0.0
info:
  title: User Management API
  version: 1.0.0
  description: 用户管理API接口文档

paths:
  /users:
    get:
      summary: 获取用户列表
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: page_size
          in: query
          schema:
            type: integer
            default: 20
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserList'

components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        email:
          type: string
        status:
          type: string
          enum: [active, inactive]

常用调试工具

  • Postman:功能强大的API测试工具
  • Insomnia:轻量级API客户端
  • curl:命令行工具,适合快速测试
  • 浏览器开发者工具:Network面板

总结

好了,RESTful API设计规范就讲到这里。回顾一下今天学的内容:

  1. REST核心思想:URL表示资源,HTTP方法表示操作
  2. URL设计:用名词不用动词,复数形式,层级清晰
  3. HTTP方法:GET/POST/PUT/PATCH/DELETE的正确使用
  4. 状态码:合理使用2xx/4xx/5xx状态码
  5. 响应格式:统一的JSON结构
  6. 认证授权:Bearer Token+JWT
  7. 分页排序:多种分页方式,清晰的参数设计
  8. 错误处理:规范化错误码和错误格式
  9. 版本管理:URL路径版本控制
  10. 文档:使用OpenAPI规范

说真的,设计好的API不是一蹴而就的事,需要在实际项目中不断打磨。我的建议是:

  1. 先设计再写代码,别边写边改
  2. 多看看优秀API的设计(GitHub、Stripe都是好例子)
  3. 站在调用方的角度思考用户体验
  4. 文档和代码同样重要
  5. 保持一致性

关于内链方面,你可以继续学习TypeScript入门完全指南,用TypeScript能写出更健壮的API后端代码。或者学习Vue3 Composition API实战教程,了解前端如何调用这些API。

常见问题

Q:GET请求能不能带body?

A:技术上可以,但主流做法是不这么做。很多HTTP客户端和代理会忽略GET请求的body。建议GET请求的参数都用query string传递。

Q:PUT和PATCH有什么区别?

A:PUT要求提供完整资源数据,缺失字段会被清空或设为默认值。PATCH只需要提供要修改的字段,其他字段保持不变。

Q:POST和PUT都能创建资源,选哪个?

A:POST用于创建资源,服务器自动生成ID。PUT用于创建或替换资源,通常客户端指定ID。在实际项目中,POST用于创建,PUT用于更新。

Q:如何设计批量操作的API?

A:可以用数组参数:

bash

POST /users/batch
{
    "operations": [
        {"action": "create", "data": {...}},
        {"action": "update", "id": 123, "data": {...}},
        {"action": "delete", "id": 456}
    ]
}

Q:API应该返回多少数据?字段是否需要过滤?

A:建议支持字段选择:

bash

GET /users?fields=id,name,email

只返回需要的字段,减少网络传输量。

Q:什么时候用状态码,什么时候用错误码?

A:HTTP状态码表示请求级别的结果(成功、认证失败、资源不存在等),应用错误码表示业务逻辑的错误(余额不足、库存不足等)。两者配合使用。

希望这篇教程对你有帮助。如果有问题或建议,欢迎在评论区交流!

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注