跳到主要内容RESTful API 接口设计规范 | 极客日志编程语言AI大前端java
RESTful API 接口设计规范
RESTful API 接口规范。首先解释了 REST 的含义,即表现层状态转移,强调其并非强制标准而是设计风格。接着阐述了构造 RESTful API 的方法,包括使用名词表示资源、通过 HTTP 方法(GET、POST、PUT、DELETE 等)定义动作、利用 URL 参数进行筛选分页以及版本控制和无状态设计。文章还列举了 RESTful 的六大约束,如客户端 - 服务器分离、无状态、可缓存等。最后介绍了开发与测试手段,推荐使用主流框架、IDE 插件及 AI 工具辅助生成代码,并强调了统一响应格式和 HTTP 状态码的重要性。遵循 RESTful 规范有助于统一接口、提高效率并降低沟通成本。
追风少年1 浏览 一、RESTful API 接口规范
REST:英文 REpresentational State Transfer 直译为表现层状态转移
RE(Representational) 表现层
RE(Representational) 表现层是指资源 (Resource) 的表现形式,
资源 (Resource) 指要操作的数据对象,比如用户、订单、商品、分类、文章等。用户列表是一个资源;某个具体的用户也是一个资源。表现层是指资源呈现出来的具体格式。比如同一个用户资源,可以用 JSON 格式返回给客户端,也可以用 XML 格式返回,这就是不同的表现形式。
S(State) 状态
ST(State) 状态是指资源在某一时刻的状态。比如你登录网站后,服务器会在内存中记住你是谁,之后在网站上操作就不用再次登录了,这就是有状态。而无状态就是服务器不记录客户端的任何信息,每次请求都是独立的。
T(Transfer) 转移
要注意转移是双向的,当你用 GET 请求时,服务器把资源的状态转移给客户端;当你用 POST 请求时,客户端把资源的新状态转移给服务器,从而改变服务器上资源的状态。
组合起来 REST(Representational State Transfer) 是一种软件架构风格,让客户端和服务器通过统一的接口以无状态的方式互相传递资源的表现层数据来查询或变更资源状态,而 ful 是个后缀就像 powerful 充满力量的一样,表示充满某个特性的。因此 RESTful API 是指符合 REST 架构风格的 API。
注意:它不是协议,不是标准,不是强制规范,只是一种建议的设计风格,你可以遵循,也可以不遵循。
例如:你要做一个用户管理系统,对用户信息进行增删改查,用 RESTful 风格的 API,如下所示:
GET /users/123 获取 ID 为 123 的用户
POST /users 创建新用户
PUT /users/123 修改用户 123
DELETE /users/123 删除用户 123
比较
| 操作 | 非 RESTful | RESTful |
|---|
| 获取用户 | POST /getUser?id=1 | GET /users/1 |
| 创建用户 | POST /createUser | POST /users |
| 更新用户 | POST /update_user | PUT /users/1 |
| 删除用户 | POST /delete/user | DELETE /users/1 |
二、构造 RESTful API
2.1 客户端发送请求
2.1.1 确定资源
资源用 URI 统一资源标识符来表示,核心规则是用 名词 来表示资源,不用动词。
微信扫一扫,关注极客日志
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
/users 表示用户列表;
/products 表示商品列表
具体某个资源,就加上 ID,如:
/users/123 表示 ID 为 123 的用户
支持嵌套,如:
/users/123/orders 获取用户 123 的所有订单
2.1.2 选择动作
是指处理资源的方式,RESTful API 主要通过不同的 HTTP 方法 来表示增删改查操作。
GET /users/123 获取 ID 为 123 的用户
POST /users 创建新用户
PUT /users/123 修改用户 123
DELETE /users/123 删除用户 123
GET /users — 查询所有用户
GET /users/123 — 查询 ID 为 123 的用户
PUT 完整更新资源
调用时需要提供资源的所有字段,多次执行结果相同(幂等性)。
PUT /users/123— 完整更新用户 123
PATCH /users/123 — 只更新用户 123 的某些字段
DELETE /users/123 — 删除用户 123
2.1.3 添加查询条件 (可选)
GET https://api.example.com/users
- 分页:
/users?page=2&limit=10 — 查询第 2 页,每页 10 条用户数据
- 筛选:
/users?gender=male&age=25 — 查询性别为男性,年龄为 25 的用户
- 排序:
/users?sort=created_at&order=desc — 按照创建时间倒序排列用户
查询参数本身不是 RESTful 特有的,但 Restful 风格强调把 筛选、排序、分页 这些操作都通过 URL 参数来表达,而不是在请求体里面传一堆复杂的 Json 对象。
GET https://api.example.com/users?age=25&page=2&sort=created_at
- 筛选 age=25
- 分页 page=2
- 排序 sort=created_at
非 Restful 风格 - JSON 对象 - 分页
{"operation":"getUsers","params":{"pagination":{"pageNumber":1,"pageSize":10}}}
非 Restful 风格 - JSON 对象 - 过滤
{"action":"searchUsers","filters":[{"field":"gender","operator":"equals","value":"male"},{"field":"age","operator":"equals","value":"25"}]}
非 Restful 风格 - JSON 对象 - 排序
{"command":"listUsers","options":{"sortBy":"create_at","sortOrder":"desc"}}
Restful 风格的 筛选、排序、分页 在 URL 更清晰明了,而且 浏览器、CDN、代理服务器 都能直接根据 URL 来缓存响应结果,不同的 URL 可以分别缓存,更容易区分。
2.1.4 版本控制
随着业务发展,接口需要更新升级,为了不影响老用户,可以在 URI 中标明版本,这样老用户用 v1 的接口,新用户用 v2 的接口,互不影响。
/v1/users — 第一版用户接口
/v2/users — 第二版用户接口
GET http://api.example.com/v2/users?age=25&page=2&sort=created_at
2.1.5 保持无状态
无状态 (Stateless) 是 RESTful 架构的核心约束之一。 其含义是,服务器不应保存客户端的会话状态。每个来自客户端的请求都必须包含处理该请求所需的全部信息(如身份凭证、上下文等)。
- 用户登录后,服务器生成一个唯一的身份凭证(如
Token),并将其返回给客户端。
- 此后客户端的每一个请求都必须在 HTTP 标头(如
Authorization: Bearer <token>)中携带此凭证。
- 服务器不关心请求之间的关联,仅根据当前请求提供的
Token 和参数进行验证、处理并返回响应。
GET /orders Header: Authorization: Bearer xxx
- 极高的可扩展性: 由于请求完全独立,无需会话亲和性(Session Affinity),任何服务器实例都能处理任何请求。这使得通过简单地增加服务器节点来实现水平扩展变得非常容易。
- 简化服务器设计: 服务器无需实现复杂的状态同步、会话存储和清理机制,降低了复杂度和资源消耗。
- 提升可靠性: 单点故障的影响更小。客户端请求可以无缝地由其他可用服务器处理,无需担心会话丢失。
- 便于缓存和负载均衡: 无状态的请求使得 HTTP 缓存机制(如 CDN、反向代理)和标准的负载均衡策略能更高效地工作。
因此,'无状态'是构建大规模、高性能、可伸缩分布式服务的基石,它将状态管理的责任转移给客户端,使服务器集群变得简单而强大。
2.2 服务端给出响应
2.2.1 统一响应格式
目前大多数 RESTful API 基本都使用 JSON 格式,因为轻量、容易解析,但并不是强制的,也可以用 XML 或 HTML 等格式。
<?xml version="1.0" encoding="UTF-8"?><user><id>123</id><name>张三</name><email>[email protected]</email></user>
<!DOCTYPE html><html><head><meta charset="UTF-8"><title>用户信息</title></head><body><div class="user-info "><p><strong>ID:</strong> 123</p><p><strong>姓名:</strong> 张三</p><p><strong>邮箱:</strong> [email protected]</p></div></body></html>
2.2.2 返回合适的 HTTP 状态码
响应需要带上合适的状态码,前端开发者或运维人员通过查看状态码,就能快速定位问题方向。
- 1xx 系列:信息提示(用的少,了解即可)
- 2xx 系列:成功
200 OK:请求成功,正常返回数据(用于 GET、PUT、PATCH)
201 Created:创建成功 (用于 POST 请求),通常会在响应头的 Location 中返回新的资源地址 URI
202 Accepted:请求已接受,但还在处理中(常用于异步操作)
204 No Content:请求成功,但无返回数据(常用于 DELETE)
- 3xx 系列:重定向
301 Moved Permanently:请求的资源已永久移动到新位置
302 Found:请求的资源已临时移动到新位置
304 Not Modified:资源未修改,客户端可以使用缓存。
- 4xx 系列:客户端错误
400 Bad Request:请求参数格式错误
401 Unauthorized:为验证身份,需要登录
403 Forbidden:已认证但没有权限访问
404 Not Found:资源不存在
405 Method Not Allowed:请求方法不被允许(比如对只读资源用 DELETE)
409 Conflict:请求冲突(比如尝试创建已存在的资源)
422 Unprocessable Entity:请求格式正确,但语义错误(比如邮箱格式不对)
429 Too Many Requests:请求过于频繁,触发限流
- 5xx 系列:服务器错误
500 Internal Server Error:服务器内部错误
502 Bad Gateway:网关错误(服务器作为网关时,从上游服务器收到无效响应)
503 Service Unavailable:服务器暂时不可用 (通常是服务器维护或过载)
504 Gateway Timeout:网关超时
三、RESTful 六大约束
1. Client-Server(客户端 - 服务器分离)
前后端各干各的活,前端负责展示,后端负责数据处理,互不干扰。
2. Stateless(无状态)
每次请求都是独立的,服务器不保持客户端的会话信息,必要信息都在请求中。
3. Cacheable(可缓存)
服务器的响应可以被标记为可缓存,客户端可以重用缓存数据,减少服务器压力。提升性能。
4. Layered System(分层系统)
客户端不需要知道直接连接的是服务器还是中间层,系统可以灵活加代理、网关、负载均衡器等。
5. Uniform Interface(统一接口)
所有资源都通过统一的接口访问,降低理解成本,提高可维护性。
6. Code-On-Demand(按需代码)
可选性,服务器可以返回可执行代码(比如 JavaScript)给客户端执行,但实际工作中很少用。
很少有 API 能完美符合所有约束,大家可以灵活自行组合。甚至可以想下面所示代码一样,都用 POST + 动词 一把梭。
app.post('/user', (req,res)=>{const{do:data}=req.body; res.json({do:result:db[do](data)});})
@app.post('/order') def handle(req):return globals()[f"{req.verb}_order"](req.data)
app.post('/file',(req,res)=>{const{act,...args}=req.body; res.json({act,data:actions[act](args)});})
只要团队达成一致,舒服就行。就像刚才那个支付订单的例子。虽然用名词符合 RESTful 规范,但有同学会觉得用动词更直观易懂,也没问题。
POST /orders/123/pay
POST /orders/123/payments
四、RESTful API 开发与测试
1. 使用开发框架
几乎所有主流的开发框架都支持 RESTful API 的开发,他们能帮你自动处理很多细节。比如:
Java 的 Spring Boot
Python 的 Django REST Framework
Go 的 Gin
这些框架虽然不强制你遵循 RESTful,但用他们的特性开发起来轻松又规范,帮你省掉大量重复代码。
IDEA 的 RESTful Toolkit 插件,可以快速查看和测试接口
VSCode 的 REST Client 插件,可以直接在编辑器里测试接口
3. 利用 AI 生成
RESTful 有明确的设计规范,而 AI 最擅长处理这种有章可循的东西,比如直接使用 AI 工具生成代码。
帮我用 Spring Boot,写一个用户管理的 RESTful API
package com.example.usermanagement.controller;
import com.example.usermanagement.dto.UserRequest;
import com.example.usermanagement.dto.UserResponse;
import com.example.usermanagement.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
@Tag(name = "用户管理", description = "用户管理相关 API")
public class UserController {
private final UserService userService;
@PostMapping
@Operation(summary = "创建用户")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<UserResponse> createUser(@Valid @RequestBody UserRequest userRequest) {
UserResponse userResponse = userService.createUser(userRequest);
return ResponseEntity.status(HttpStatus.CREATED).body(userResponse);
}
@GetMapping("/{id}")
@Operation(summary = "根据 ID 获取用户")
public ResponseEntity<UserResponse> getUserById(@Parameter(description = "用户 ID") @PathVariable Long id) {
UserResponse userResponse = userService.getUserById(id);
return ResponseEntity.ok(userResponse);
}
@GetMapping("/username/{username}")
@Operation(summary = "根据用户名获取用户")
public ResponseEntity<UserResponse> getUserByUsername(@Parameter(description = "用户名") @PathVariable String username) {
UserResponse userResponse = userService.getUserByUsername(username);
return ResponseEntity.ok(userResponse);
}
@GetMapping
@Operation(summary = "获取用户列表(分页)")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<Page<UserResponse>> getAllUsers(@PageableDefault(size = 20, sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable) {
Page<UserResponse> users = userService.getAllUsers(pageable);
return ResponseEntity.ok(users);
}
@PutMapping("/{id}")
@Operation(summary = "更新用户信息")
public ResponseEntity<UserResponse> updateUser(@Parameter(description = "用户 ID") @PathVariable Long id, @Valid @RequestBody UserRequest userRequest) {
UserResponse userResponse = userService.updateUser(id, userRequest);
return ResponseEntity.ok(userResponse);
}
@PatchMapping("/{id}/status")
@Operation(summary = "更新用户状态")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<UserResponse> updateUserStatus(@Parameter(description = "用户 ID") @PathVariable Long id, @RequestParam Boolean isActive) {
UserResponse userResponse = userService.updateUserStatus(id, isActive);
return ResponseEntity.ok(userResponse);
}
@DeleteMapping("/{id}")
@Operation(summary = "删除用户")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<Void> deleteUser(@Parameter(description = "用户 ID") @PathVariable Long id) {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
}
4. 生成文档
写完接口后还可以用 Swagger 这类工具,自动生成漂亮的文档。直接甩给前端,对方一看就懂,还能在线测试接口,省去大量沟通成本。
五、总结