结构:

  • 引言:什么是RESTful API
  • REST架构风格的核心原则
  • RESTful API的资源设计
  • 状态码
  • 请求和响应格式
  • 实战案例

引言:什么是RESTful API?

在现代 Web 和移动应用的开发中,前后端分离已成为主流开发模式。在这种架构中,前端负责 UI 展示,后端则通过 API 向前端提供数据接口。而提到 API,RESTful API 几乎是最常被提及的关键词之一。

REST,全称 Representational State Transfer,是一种架构风格,而不是具体的协议和技术。它由计算机科学家 Roy Fielding 在他的博士论文中提出,旨在设计更清晰、可扩展、结构化的 Web 服务。一个遵循 REST 原则的 API,通常被称为 “RESTful API”。

那么问题来了:

  • RESTful API到底"长什么样"?
  • 它为什么会成为主流?
  • 在日常开发中,我们该如何设计一个既优雅又实用的RESTful API?

这篇文章将带你从基础概念出发,一步步了解REST的设计理念、资源路由规范、HTTP方法的语义、状态码的使用方式,以及常见的设计技巧和最佳实践。


REST架构风格的核心原则

我们知道REST并不是一种具体的技术和协议,而是一种"架构风格",它使得API更加规范、易用、可维护。下面是 REST 的六大核心原则。

  1. 无状态(Stateless) REST 要求每一个请求都必须是自包含的:服务器不会在不同请求之间存储任何客户端的状态信息。换句话说,客户端的每个请求都必须包含完成该请求所需的全部信息(显示传入)。

    举例说明:登录后Client发送请求应在Header中附带token,而不是依赖服务器的Session。

    那么为什么要这样做呢?

    • Scalability:无状态设计让任何一个服务器节点都可以处理任何一个请求,因为请求中自带了所有信息。这为负载均衡和自动扩容提供了天然支持。 举例:如果一个系统部署了三个后端服务节点(A/B/C),在无状态的设计下,负载均衡器可以将任意请求分发到任一节点,完全不需要考虑"这个用户上次是在哪台服务器登录的"
    • Easy Deployment:由于请求之间没有状态依赖,服务节点可以随时上下线,不会影响用户体验,适合微服务架构或容器化部署(如 Docker/Kubernetes)。
    • 故障恢复更简单:当服务器崩溃时,不需要恢复任何"用户会话"或"中间状态",只需要重新启动实例即可继续处理请求。客户端只需要重新发起请求,不必担心状态丢失。
    • 更高的可测试性和可维护性:测试 RESTful 接口时,每个请求都是独立的,可以单独构造和验证,不需要手动维护前置状态,降低了集成测试和自动化测试的复杂度。
  2. 统一接口(Uniform Interface) 统一接口是REST最核心的思想之一,它确保了客户端和服务器之间的通信的一致性和可预期性,从而降低系统复杂度。换句话说,不管你访问的是"用户信息"、“文章评论"还是"订单数据”,它们的请求风格应该看起来是类似的。 其中包括:

    • 使用标准的 HTTP 方法(GET、POST、PUT、DELETE)
    • 使用资源的 URI 来表示数据(如 /users/123)
      • 资源应通过URI来唯一标识,而不是通过动词。
      • ✅例子:
        • 获取所有用户:GET /users
        • 获取某个用户:GET /users/123
        • 获取用户的订单:GET /users/123/orders
      • ❌例子:
        • /getUserById?id=123
        • /doCreateUser
      • 这里需要注意URI中含有 “?”, 不一定代表不符合REST,需要区分:
        • 查询参数用于过滤、分页 - ✅ 合理
        • 查询参数用于标识资源 - ❌ 不推荐
    • 使用标准的响应格式(如 JSON)
    • 使用统一的状态码表示处理结果(如 200 OK, 404 Not Found)
  3. 可缓存(Cacheable) 在 REST 架构中,服务器响应应该显式地标明是否可以被缓存、缓存多久、缓存的条件等。 💡 REST 的可缓存性意味着客户端或中间代理(如浏览器、CDN、反向代理)可以在某些条件下缓存响应结果,从而减少重复请求,提升性能。 可缓存的优势: 1. 减少服务器负载:当客户端或中间层缓存了响应数据,后续相同请求可以直接返回缓存内容,无需再次访问数据库或计算逻辑。 2. 提高响应速度:缓存一般在本地或边缘节点,响应时间远小于重新生成内容。 3. 带宽优化:对于高频访问的数据(如文章列表、商品页等),缓存可以大幅减少网络传输和 I/O 开销。 REST架构基于HTTP,因此天然可以使用HTTP的缓存机制:

    • Cache-Control: public, max-age=3600
    • ETag & If-None-Match 实现条件缓存
    • Last-Modified 和 If-Modified-Since
  4. 客户端-服务端分离(Client-Server)

    • 客户端:展示 UI、获取用户输入、发送请求;
    • 服务端:处理请求、执行业务逻辑、访问数据库、返回数据;
    • 前端通过HTTP请求与后端交互;
    • 后端根据请求返回JSON格式数据;
    • 前端将数据渲染成页面展示;
  5. 分层系统(Layered System) 这一点相较于其他点,有些抽象,它旨在强调系统通过层与层之间的清晰分工与接口隔离,实现解耦、灵活扩展和安全性提升。在实际代码中,体现这一原则主要通过合理的项目结构设计和层间职责划分完成。

    层次划分为:

    1. 客户端层(Client)
      • 负责发起请求、接收响应、渲染界面
      • 不关心服务端内部实现
    2. 接口层(API 层)
      • 对外暴露统一接口(RESTful API),接收请求
      • 处理请求路由、参数校验、响应格式化
    3. 业务逻辑层(Service层)
      • 负责核心业务处理,比如订单管理、用户权限控制。
      • 只专注业务流程,不直接操作数据库
    4. 数据访问层(DAO层)
      • 负责与数据库交互,执行 CRUD 操作
      • 负责持久化细节封装
    5. 中间件层(Middleware)
      • 处理跨层的功能,如身份验证、限流、日志、缓存
      • 对请求或响应进行拦截和处理
    6. 缓存层、代理层(如CDN、负载均衡)
      • 运行在服务器与客户端之间,提升性能和安全
  6. 按需代码(Code on Demand) 它是REST架构风格中唯一的可选约束,它允许服务器向客户端传输可执行代码来扩展客户端的功能。 核心概念:服务器可以通过发送可执行代码(通常是脚本)来临时增强客户端的处理能力,而不是仅仅发送静态数据。客户端接收到这些代码后可以在本地执行,从而获得新的功能或改变现有行为。 例如:

{
  "data": {...},
  "script": "function processData(data) { return data.map(item => item.value * 2); }"
}

RESTful API的资源设计

在 REST 架构中,“资源(Resource)“是核心概念。REST 并不是围绕"操作"来设计接口的,而是围绕"资源"的识别、操作、表示和状态转移来组织 API。这种设计方式清晰、直观,也便于扩展和维护

如何设计资源URI?

  1. 使用名词,复数形式
    • GET / users – 所有用户
    • GET / users / 123 – 某个用户
  2. 使用嵌套结构表示资源关系(子资源)
    • GET /users/123/posts – 某用户的文章
    • GET /posts/456/comments – 某文章的评论
  3. 不用动词
    • GET / getUserById?id=123 ❌
    • POST / createNewUser ❌

资源的表示

REST 规定了服务器通过 HTTP 响应返回资源的"表现形式”(如 JSON、XML)

例如:

GET /users/123

响应:

{
  "id": 123,
  "name": "Jacky",
  "email": "jacky@example.com"
}

可以通过 Accept 头来协商使用 JSON 或 XML: Accept: application/json

可过滤、排序、分页的资源设计(Query 参数)

虽然REST URI本身只描述资源,但查询和控制参数可以通过 ?添加,这完全符合REST设计规范。 例如: GET /posts?author=jacky&limit=10&page=2&sort=-createdAt 这表示查询 Jacky 的文章,每页 10 篇,第 2 页,按创建时间倒序排列。

资源状态的变更通过请求体完成

例如:

PATCH /users/123
{
  "email": "new@example.com"
}

统一资源路径命名规范

  • 使用小写字母
  • 资源名使用复数(/users,而不是 /user)
  • 使用连字符 - 连接多个词 (/user-profiles)
  • 避免使用下划线或驼峰

状态码

状态码用于表示服务器对请求的处理结果。REST API 应该使用语义明确的状态码,帮助客户端理解结果。

分类范围说明
成功2xx请求成功
重定向3xx客户端需采取额外操作
客户端错误4xx客户端请求错误
服务器错误5xx服务器处理失败

常见状态码详解

状态码含义用途说明
200OK请求成功(一般是 GET、PUT)
201Created创建成功(用于 POST 创建资源)
204No Content成功但无内容(如 DELETE 成功)
400Bad Request请求无效,如缺参数、格式错误等
401Unauthorized未授权,缺少或无效的认证信息
403Forbidden有权限机制,但被禁止访问
404Not Found资源不存在
409Conflict资源冲突,如重复创建
422Unprocessable Entity请求格式正确,但语义错误
500Internal Server Error服务器内部错误

请求与响应格式

对于REST风格的APIs来说,我们需要遵循特定的request和response格式,这样也便于我们日常的开发(更便于实现前后端交互)。

  1. 请求格式(Request)
    • 通常使用application / json格式
    • 必须设置正确的头部:Content-Type: application/json
    • 请求体(POST创建用户)
POST /users
Content-Type: application/json

{
  "name": "Jacky",
  "email": "jacky@example.com"
}
  1. 响应格式(Response)
    • 响应体通常是 application / json
    • 建议包括:
      • 状态信息(code/message)
      • data
      • error info(可选)
{
  "code": 200,
  "message": "Success",
  "data": {
    "id": 123,
    "name": "Jacky"
  }
}

实战案例:简易博客系统

我们要为博客系统设计一组 API,满足以下需求:

  • 用户注册、登录、查看信息
  • 创建、查看、更新、删除文章
  • 给文章添加评论
  • 支持分页、排序和筛选
  • 基于 Token 的用户鉴权

资源建模

实体路径描述
用户 users/users管理用户账号
登录 login/auth/login用户登录,获取 JWT Token
文章 posts/posts博客文章资源
评论 comments/posts/:id/comments某篇文章的评论

API路由设计

  • 👤用户相关API
方法路径描述状态码
POST/users注册用户201
GET/users/:id获取用户资料200
POST/auth/login用户登录200/401
  • 📝 文章相关接口 支持分页、筛选 GET /posts?page=2&limit=10&author=jacky&sort=-createdAt
方法路径描述状态码
GET/posts获取文章列表(分页)200
GET/posts/:id获取文章详情200/404
POST/posts创建文章201
PATCH/posts/:id更新文章部分字段200
DELETE/posts/:id删除文章204/404
  • 💬 评论相关接口(子资源)
方法路径描述状态码
POST/posts/:id/comments给文章添加评论201
GET/posts/:id/comments获取文章评论列表200
  • 鉴权机制(JWT) 所有需要登录到请求必须携带Token: Authorization: Bearer <jwt_token> Server校验Token,并将用户信息附加到请求上下文(req.user)

前段时间做了个OA,其中考到REST API的URI设计规范,因此写了这篇博客好好地学习一下。