当前位置: 首页 > news >正文

RESTful API

restful本质上是一种设计风格,没有特别的规定,主要是为了创建高效,安全,易于使用和维护的API

RESTful API 全面详解

什么是 RESTful API?

RESTful API 是一种基于 REST(Representational State Transfer,表述性状态转移)架构风格设计的 API。它不是标准或协议,而是一种设计风格,用于创建可扩展、可靠且易于维护的 Web 服务。

核心概念

  • 资源(Resource):网络上的任何事物都是资源,如用户、订单、产品等

  • 表述(Representation):资源的具体表现形式,如 JSON、XML 等

  • 状态转移(State Transfer):通过操作资源的表述来实现资源状态的改变

REST 的 6 大约束条件

1. 客户端-服务器架构(Client-Server)

  • 关注点分离:客户端负责用户界面,服务器负责数据处理和存储

  • 允许两者独立演进

2. 无状态(Stateless)

  • 每个请求必须包含处理该请求所需的所有信息

  • 服务器不保存客户端的状态信息

  • 优点:可扩展性、可靠性、简化服务器设计

3. 可缓存(Cacheable)

  • 响应必须明确表明是否可缓存

  • 减少客户端-服务器交互,提高性能

4. 统一接口(Uniform Interface)

  • 资源标识:使用 URI 标识资源

  • 通过表述操作资源:使用标准 HTTP 方法

  • 自描述消息:消息包含足够信息说明如何处理

  • 超媒体作为应用状态引擎(HATEOAS):响应中包含相关操作的链接

5. 分层系统(Layered System)

  • 客户端不知道是否直接连接到最终服务器

  • 中间层(如负载均衡器、代理)可以提高可扩展性和安全性

6. 按需代码(Code-On-Demand,可选)

  • 服务器可以临时扩展客户端功能,如发送 JavaScript 代码

RESTful API 设计规范

1. 资源命名规范

使用名词而非动词

http

# 正确 - 使用名词
GET /users
GET /users/123
POST /users# 错误 - 使用动词
GET /getUsers
POST /createUser
使用复数形式

http

# 推荐使用复数
GET /users
GET /users/123/orders# 单数形式(不推荐)
GET /user
GET /user/123/order
资源层次结构

http

# 表示层次关系
GET /users/123/orders          # 用户123的所有订单
GET /users/123/orders/456      # 用户123的订单456
过滤、排序、分页

http

# 使用查询参数
GET /users?role=admin&active=true      # 过滤
GET /users?sort=name,-age              # 排序(name升序,age降序)
GET /users?page=2&limit=20             # 分页
GET /users?fields=id,name,email        # 字段选择

2. HTTP 方法使用规范

HTTP 方法描述幂等性安全性
GET获取资源
POST创建资源
PUT更新或创建资源
PATCH部分更新资源
DELETE删除资源
具体使用示例

http

# 获取资源
GET /users/123# 创建资源
POST /users
Content-Type: application/json
{"name": "John Doe","email": "john@example.com"
}# 完整更新资源
PUT /users/123
Content-Type: application/json
{"name": "John Smith","email": "john.smith@example.com"
}# 部分更新资源
PATCH /users/123
Content-Type: application/json
{"email": "new.email@example.com"
}# 删除资源
DELETE /users/123

 Content-Type(内容类型)与 Accept(接受类型)

特性Content-TypeAccept
方向主要描述请求体格式描述期望的响应格式
请求中声明客户端发送数据的格式声明客户端希望接收数据的格式
响应中声明服务器返回数据的格式一般不用于响应
必需性有请求体时通常必需可选,但有最佳实践价值

实际开发中的应用

1. 正确的 API 调用示例

javascript

// 前端代码调用 API
async function updateUser(userId, userData) {const response = await fetch(`/api/users/${userId}`, {method: 'PUT',headers: {'Content-Type': 'application/json', // 我发送的是 JSON'Accept': 'application/json'        // 我希望接收 JSON},body: JSON.stringify(userData)});return response.json();
}

3. HTTP 状态码使用

2xx 成功
  • 200 OK:请求成功

  • 201 Created:资源创建成功

  • 202 Accepted:请求已接受处理,但尚未完成

  • 204 No Content:请求成功,但无返回内容

3xx 重定向
  • 301 Moved Permanently:资源已永久移动

  • 302 Found:资源临时移动

  • 304 Not Modified:资源未修改(缓存相关)

4xx 客户端错误
  • 400 Bad Request:请求格式错误

  • 401 Unauthorized:需要身份验证

  • 403 Forbidden:无权限访问

  • 404 Not Found:资源不存在

  • 405 Method Not Allowed:HTTP方法不允许

  • 409 Conflict:资源状态冲突

  • 429 Too Many Requests:请求过于频繁

5xx 服务器错误
  • 500 Internal Server Error:服务器内部错误

  • 501 Not Implemented:功能未实现

  • 503 Service Unavailable:服务不可用

4. 版本管理

API 版本管理是 RESTful API 设计中至关重要的一环,它允许 API 在演进过程中引入破坏性变更,同时保持对旧客户端的兼容性。

URI 路径版本控制(最常用)

http

GET /api/v1/users
GET /api/v2/users
  • 简单直观:URI 中明确显示版本号,易于理解和调试

  • 易于实现:服务器可以根据路径路由到不同版本的处理器

  • 缓存友好:不同版本的资源有不同 URI,可以分别缓存

  • 浏览器友好:可以直接在浏览器中测试不同版本的 API

缺点
  • URI 污染:版本信息污染了干净的 URI 设计

  • 破坏 REST 原则:理论上,资源的 URI 不应该随时间变化

  • 维护成本:需要维护多个版本的端点

实际应用示例

python

# Flask 框架中的实现示例
@app.route('/api/v1/users')
def get_users_v1():# 版本1的实现return jsonify({"version": "v1", "data": [...]})@app.route('/api/v2/users')  
def get_users_v2():# 版本2的实现return jsonify({"version": "v2", "data": [...], "metadata": {...}})

公司中的实际使用

大多数公司选择这种方式,因为:

  • 开发工具和监控系统更容易处理

  • 可以在负载均衡器级别进行版本路由

  • 便于 A/B 测试和灰度发布

请求头版本控制(更 RESTful 的方式)

http

GET /users
Accept: application/vnd.example.v1+json
查询参数版本控制

http

GET /users?version=1
实现方式

http

# 使用 Accept 头指定版本
GET /users
Accept: application/vnd.example.v1+jsonGET /users  
Accept: application/vnd.example.v2+json# 或者使用自定义头
GET /users
X-API-Version: 1
媒体类型版本控制(推荐)

http

GET /users/123
Accept: application/vnd.company.user.v1+jsonGET /users/123
Accept: application/vnd.company.user.v2+json
优点
  • 干净的 URI:URI 不包含版本信息,更符合 REST 原则

  • 内容协商:符合 HTTP 的内容协商机制

  • 渐进式升级:客户端可以逐步迁移到新版本

缺点
  • 复杂性:服务器需要解析请求头来确定版本

  • 调试困难:不能在浏览器中直接测试,需要特殊工具

  • 缓存挑战:相同 URI 的不同版本需要不同的缓存键

3. 查询参数版本控制

实现方式

http

# 使用查询参数指定版本
GET /users?version=1
GET /users/123?version=1GET /users?version=2  
GET /users/123?version=2
优点
  • 简单易用:只需添加查询参数,易于理解和实现

  • 渐进升级:客户端可以逐个请求迁移到新版本

  • 调试方便:可以在浏览器中直接测试

缺点
  • URI 不一致:相同资源有多个不同的 URI

  • 缓存问题:需要确保缓存系统正确处理查询参数

  • 不够 RESTful:查询参数通常用于过滤,而不是版本控制

实际应用示例

python

@app.route('/users')
def get_users():version = request.args.get('version', '1')  # 默认为版本1if version == '1':return jsonify({"version": "v1", "data": [...]})elif version == '2':return jsonify({"version": "v2", "data": [...], "metadata": {...}})

版本管理策略比较

策略优点缺点适用场景
URI 路径简单直观,易于缓存URI 污染,破坏 REST 原则大多数企业应用
请求头干净的 URI,符合 REST复杂,调试困难对 REST 原则要求严格的项目
查询参数简单易用,渐进升级URI 不一致,缓存问题简单的 API,快速原型

最佳实践建议

  1. 选择一种策略并保持一致:不要在同一个 API 中混用多种版本控制策略

  2. 提供默认版本:当客户端没有指定版本时,提供一个合理的默认版本

5. 响应格式

响应内容都是JSON体(body),但没有状态码字段——这是REST风格API常见做法。HTTP状态码(如200、400、404、500等)不是写在JSON里,而是由HTTP协议的响应头(header)直接返回的

下面只是HTTP响应体(body)的一部分,不包含状态码。

  • 前端/客户端收到响应时,先看HTTP头里的状态码,再解析body里的JSON内容。
  • 这样能区分:
    • 传输层/协议层的异常(如网络错误状态码)
    • 业务内容的异常/返回信息(如{"error": ...})
成功响应

json

{"data": {"id": 123,"name": "John Doe","email": "john@example.com"}
}
列表响应

json

{"data": [{"id": 123,"name": "John Doe"},{"id": 124,"name": "Jane Smith"}],"pagination": {"total": 2,"page": 1,"limit": 20}
}
错误响应

json

{"error": {"code": "invalid_email","message": "邮箱格式不正确","details": {"email": "invalid-email"}}
}

 实际返回流程举例

成功响应

HTTP

HTTP/1.1 200 OK
Content-Type: application/json{"data": {"id": 123,"name": "John Doe","email": "john@example.com"}
}

业务错误响应

HTTP

HTTP/1.1 400 Bad Request
Content-Type: application/json{"error": {"code": "invalid_email","message": "邮箱格式不正确","details": {"email": "invalid-email"}}
}

6. HATEOAS(超媒体作为应用状态引擎)

json

{"data": {"id": 123,"name": "John Doe","email": "john@example.com","links": [{"rel": "self","href": "/users/123","method": "GET"},{"rel": "update","href": "/users/123","method": "PUT"},{"rel": "delete","href": "/users/123","method": "DELETE"},{"rel": "orders","href": "/users/123/orders","method": "GET"}]}
}

实际开发中的重要考虑

1. 认证和授权

认证与授权在实际开发中的实现细节,尤其是JWT(JSON Web Token)认证OAuth 2.0授权

JWT(JSON Web Token)认证

JWT是一种无状态的令牌认证机制,常用于Web API和微服务。

  • 令牌本身就是用户身份和权限的信息,服务端不用存session,直接验证令牌即可。
  • 令牌内容是一个经过签名的JSON字符串,通常包括用户ID、角色、过期时间等。

2. 认证流程

步骤一:用户登录,获取JWT令牌

HTTP

POST /auth/login
Content-Type: application/json{"username": "user@example.com","password": "password123"
}
  • 客户端提交用户名和密码,服务端验证是否正确。
步骤二:服务端返回JWT令牌

JSON

{"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
  • 令牌内容由三部分组成:Header(头)、Payload(载荷)、Signature(签名)。
  • 服务端用密钥对令牌进行签名。
步骤三:客户端保存令牌(通常存localStorage或Cookie)

步骤四:客户端访问受保护接口时,带上令牌

HTTP

GET /users/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
  • 在请求头加Authorization: Bearer <token>,服务端验证令牌即可。

3. 服务端如何验证JWT?

  • 解析令牌,验证签名是否正确(确保没被伪造)。
  • 检查令牌是否过期。
  • 从令牌中读取用户信息和权限,决定是否允许访问。

4. JWT的优缺点

优点:

  • 无状态,服务端不用保存session,扩展性强。
  • 可扩展到微服务、分布式架构。
  • 可以直接存放权限、角色等信息,API可以快速做权限判断。

缺点:

  • 一旦令牌泄露,别人可以伪造身份。
  • 令牌不能主动失效(只能等到过期或用黑名单)。
  • 刷新机制要自己实现。

5. 实际开发要点

  • 令牌要设置合理的过期时间,比如15分钟~2小时。
  • 不要把敏感信息(如密码)放在JWT里
  • 密钥要妥善保管,否则令牌验证就失效了。
  • HTTPS必须开启,防止令牌被窃听。
  • 注意跨域和CSRF问题,存储方式要选对。

http

POST /auth/login
Content-Type: application/json
{"username": "user@example.com","password": "password123"
}# 响应
{"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

http

GET /users/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
OAuth 2.0

http

# 授权码流程
GET /oauth/authorize?client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&response_type=code# 获取访问令牌
POST /oauth/token
Content-Type: application/x-www-form-urlencodedgrant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=REDIRECT_URI&client_id=CLIENT_ID&client_secret=CLIENT_SECRET

1. OAuth 2.0是什么?

OAuth 2.0是第三方授权协议,常用于“用微信/QQ/Google登录”,或让第三方APP安全访问你的API。

  • 用户可以授权第三方应用访问自己的资源,而不用把密码给第三方。

2. 授权码流程(最常用)

步骤一:客户端跳转到授权页面

HTTP

GET /oauth/authorize?client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&response_type=code
  • 用户看到授权页面,输入账号密码并同意授权。
  • client_id:第三方应用的标识
  • redirect_uri:授权完成后回跳地址
步骤二:授权服务器回跳带授权码

HTTP

GET REDIRECT_URI?code=AUTHORIZATION_CODE
  • 授权码是一次性的短期凭证。
步骤三:客户端用授权码换取访问令牌

HTTP

POST /oauth/token
Content-Type: application/x-www-form-urlencodedgrant_type=authorization_code
code=AUTHORIZATION_CODE
redirect_uri=REDIRECT_URI
client_id=CLIENT_ID
client_secret=CLIENT_SECRET
  • 服务端验证授权码有效后,返回access_token(访问令牌)。
步骤四:客户端带令牌访问资源

HTTP

GET /api/resource
Authorization: Bearer ACCESS_TOKEN
  • 令牌代表用户授权给第三方的权限,服务端验证后决定是否允许访问。

3. OAuth 2.0的优缺点

优点:

  • 用户不用把密码给第三方,安全性高。
  • 可细粒度控制授权范围(只开放部分权限)。
  • 支持令牌刷新,长期授权。

缺点:

  • 实现复杂,涉及回跳、授权码、令牌管理。
  • 配置繁琐,需要注册client_id、client_secret。

4. 实际开发要点

  • 登录和授权最好分离,用户体验更好。
  • 回跳地址要严格校验,防止钓鱼和劫持。
  • 授权码和令牌要设置合理的有效期和权限范围。
  • 日志和审计要完善,方便追踪授权行为。
  • 三、JWT vs OAuth 2.0

  • JWT适用于你自己的用户认证和API鉴权(比如你的Web后端、微服务、移动APP)。
  • OAuth 2.0适用于“第三方授权”,比如开放平台、社交登录,让别人安全地访问你的数据。
  • 两者可以结合,OAuth 2.0获取到token后,令牌本身可以是JWT格式。
  • 五、工程实战建议

  • 用JWT做自己的系统认证,简单高效。
  • 对外开放API时,用OAuth 2.0,不要暴露用户密码。
  • 所有敏感操作(如支付、转账),二次认证+短期令牌。
  • 令牌管理、黑名单机制、刷新机制要设计好。

2. 速率限制(Rate Limiting)

速率限制就是API服务器每单位时间允许客户端发起的最大请求数

用来限制客户端请求频率,保护服务器资源,防止滥用或攻击

当你访问RESTful API时,服务器通常会在HTTP响应头里加入速率限制相关字段,最典型的就是:

    http

    # 响应头中包含速率限制信息
    X-RateLimit-Limit: 1000
    X-RateLimit-Remaining: 999
    X-RateLimit-Reset: 1627833600

    1. X-RateLimit-Limit

    • 含义:你每个时间窗口(比如1小时、1分钟)最多能请求多少次API。
    • 例子1000表示你每小时最多能发1000次请求。

    2. X-RateLimit-Remaining

    • 含义:你在当前时间窗口内还剩下多少次可用请求。
    • 例子999表示你还有999次可用(刚刚用了1次)。

    3. X-RateLimit-Reset

    • 含义:时间窗口什么时候重置,单位通常是Unix时间戳(秒)。
    • 例子1627833600表示到这个时间点后,速率限制会重置,你又有1000次可用。
    • 可以用date -d @1627833600转换为人类可读时间。

    比如你的API速率限制是每小时最多1000次

    1. 你第一次请求,响应头里显示:
      • X-RateLimit-Remaining: 999
    2. 你又请求了10次,就变成:
      • X-RateLimit-Remaining: 989
    3. 一小时内你请求满了1000次,下一次再请求时:
      • 有的API会返回429 Too Many Requests状态码,表示超出速率限制。
      • 响应头里可能显示剩余为0,reset时间戳告诉你什么时候可以重新请求。
    1. API客户端每次请求都应该解析这些响应头
      • 动态判断还有多少额度,是否要等待
    2. 当剩余为0或快到0时,要主动限流或延迟请求,避免被封禁
    3. 遇到429 Too Many Requests时,可以用X-RateLimit-Reset字段等待重试

    代码示例:

    Python

    import requests
    import timeresp = requests.get("https://api.example.com/data")
    limit = int(resp.headers.get("X-RateLimit-Limit", "0"))
    remaining = int(resp.headers.get("X-RateLimit-Remaining", "0"))
    reset = int(resp.headers.get("X-RateLimit-Reset", "0"))if remaining == 0:print("Rate limit exceeded, wait until:", time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(reset)))time.sleep(max(0, reset - int(time.time())))
    else:print(f"You have {remaining}/{limit} requests left")

    五、速率限制的常见策略

    • 固定窗口(Fixed Window):每个时间窗口限制请求数,窗口到期重置。
    • 滑动窗口(Sliding Window):更细致,按时间间隔移动窗口。
    • 令牌桶/漏桶算法(Token Bucket/Leaky Bucket):可平滑突发流量。
    • 按用户/按IP/按API Key限流:不同身份不同额度。

    六、工程注意事项

    • 速率限制通常是后端统一配置,前端只需要遵守和解析头信息。
    • 超出速率限制时,API一般返回429 Too Many Requests,要处理重试逻辑。
    • 有些API会在错误响应的body里写明超额信息,但头部字段是标准做法。

    七、总结

    • X-RateLimit-Limit:本窗口最大允许请求数
    • X-RateLimit-Remaining:本窗口剩余可用请求数
    • X-RateLimit-Reset:下一个窗口的起始时间(Unix时间戳)
    • 客户端应主动解析这些头,合理限流、避免封禁、提升体验

    3. API 文档

    OpenAPI/Swagger 规范

    yaml

    openapi: 3.0.0
    info:title: User APIversion: 1.0.0
    paths:/users:get:summary: 获取用户列表parameters:- name: pagein: queryschema:type: integerdescription: 页码responses:'200':description: 成功content:application/json:schema:type: objectproperties:data:type: arrayitems:$ref: '#/components/schemas/User'pagination:$ref: '#/components/schemas/Pagination'

    一、什么是 OpenAPI/Swagger?

    • OpenAPI(前身Swagger)规范是描述RESTful API的标准格式,通常用YAML或JSON书写。
    • 它可以被自动工具识别,生成可交互的API文档(如 Swagger UI),还可用于代码生成、测试和mock服务。
    • 让API描述规范化、自动化、可读性强,极大提升开发效率和协作体验。

    二、这段API文档的结构详解

    YAML

    openapi: 3.0.0
    
    • 标明使用的是 OpenAPI 3.0.0 版本。

    YAML

    info:title: User APIversion: 1.0.0
    
    • info部分:API的基本信息
      • title:API名称
      • version:API版本号

    YAML

    paths:/users:get:summary: 获取用户列表parameters:- name: pagein: queryschema:type: integerdescription: 页码responses:'200':description: 成功content:application/json:schema:type: objectproperties:data:type: arrayitems:$ref: '#/components/schemas/User'pagination:$ref: '#/components/schemas/Pagination'
    

    1. paths

    • paths:API的所有路由(URL)定义区
    • 每个path对应一个API资源或接口

    2. /users 路径

    • /users:表示一个用户相关的资源集合,通常用于管理用户列表

    3. GET 方法

    • get:HTTP GET方法,对应“查询用户列表”
    3.1 summary
    • summary: 获取用户列表
      • 简要说明接口用途,Swagger UI会展示这里的内容。
    3.2 parameters
    • 参数定义区,说明本接口支持哪些参数

    YAML

    parameters:- name: pagein: queryschema:type: integerdescription: 页码
    
    • name: page:参数名叫 page
    • in: query:参数在查询字符串(如 /users?page=2)
    • type: integer:参数类型为整数
    • description: 页码:参数说明

    3.3 responses
    • responses:定义请求的所有响应类型(通常有200、400、404等)

    YAML

    '200':description: 成功content:application/json:schema:type: objectproperties:data:type: arrayitems:$ref: '#/components/schemas/User'pagination:$ref: '#/components/schemas/Pagination'
    
    • '200':HTTP状态码 200,表示成功
    • description: 成功:响应说明
    3.3.1 content
    • content: application/json:响应体格式为JSON
    3.3.2 schema
    • schema:定义响应JSON的结构(数据模型)

    YAML

    type: object
    properties:data:type: arrayitems:$ref: '#/components/schemas/User'pagination:$ref: '#/components/schemas/Pagination'
    
    • 响应是一个对象(即JSON的 {} 结构)
    • 有两个属性:
      • data:一个数组,每个元素是一个 User 对象(引用了 User 模型)
      • pagination:分页信息,引用了 Pagination 模型

    4. $ref 语法

    • $ref是OpenAPI的引用语法,指向文档下方components/schemas定义的数据模型。
    • 比如#/components/schemas/User表示引用名为User的数据结构。
    • 这让模型可以复用,结构清晰。

    三、实际工程意义

    1. 前后端协作标准

      • API接口结构、字段、参数、响应都规范化,前后端无需反复确认,减少沟通成本。
    2. 自动化文档和测试

      • 用Swagger UI等工具加载OpenAPI文档,自动生成可交互的API页面,开发、测试、产品、运维都可用。
      • 可直接在线调试接口,输入参数,查看响应。
    3. 代码自动生成

      • 后端可根据OpenAPI文档生成接口框架代码,前端可生成API调用代码,减少重复劳动。
    4. Mock和自动化测试

      • 可用文档自动生成Mock服务,前端可在后端未完成时提前开发和测试。

    四、扩展:components/schemas

    通常会在文档后面补充数据模型定义:

    YAML

    components:schemas:User:type: objectproperties:id:type: integername:type: stringemail:type: stringPagination:type: objectproperties:total:type: integerpage:type: integerlimit:type: integer
    

    五、总结

    • OpenAPI/Swagger文档是描述RESTful API最权威的标准
    • 明确接口路径、参数、响应结构、数据模型
    • 支持自动化文档、代码生成、测试和Mock
    • 前后端协作、API治理、API扩展都极为高效

    4. 数据验证

    json

    // 请求体验证
    {"type": "object","required": ["name", "email"],"properties": {"name": {"type": "string","minLength": 1,"maxLength": 100},"email": {"type": "string","format": "email"},"age": {"type": "integer","minimum": 0,"maximum": 150}}
    }

    一、数据验证(Validation)

    1. 为什么要做数据验证?

    • 防止不合法、恶意、脏数据进入系统
    • 避免后端业务/数据库报错
    • 前端用户体验友好,第一时间给出清晰提示
    • 提升系统安全性和健壮性

    2. 请求体验证规范解析

    你给的JSON是典型的JSON Schema,很多API框架(如Node.js的express-validator、Python的pydantic/FastAPI、Java的Hibernate Validator)都支持类似规范。

    JSON

    {"type": "object","required": ["name", "email"],"properties": {"name": {"type": "string","minLength": 1,"maxLength": 100},"email": {"type": "string","format": "email"},"age": {"type": "integer","minimum": 0,"maximum": 150}}
    }
    

    逐项解释

    • "type": "object"
      请求体必须是一个对象(即{}结构),不是数组或其他类型。

    • "required": ["name", "email"]
      nameemail字段是必填项,缺少即报错。

    • "properties"
      对每个字段进行详细约束:

      • "name"

        • 必须是字符串
        • 长度至少1,最多100字符
        • 空字符串或超长字符串都不合法
      • "email"

        • 必须是字符串
        • format: "email"要求有正确的邮箱格式
        • 比如haojiubudaqiu@github.com是合法,hello@abc是不合法
      • "age"

        • 必须是整数
        • 最小0,最大150
        • 负数或超过150都不合法
        • 非必填,可以为空

    3. 实际开发怎么用?

    • 后端收到请求体后,先用这些规则做校验
    • 不通过则立即返回错误响应,不进入业务处理
    • 很多API框架都能自动根据JSON Schema校验
    示例:Python FastAPI

    Python

    from pydantic import BaseModel, EmailStr, conint, constrclass UserRequest(BaseModel):name: constr(min_length=1, max_length=100)email: EmailStrage: conint(ge=0, le=150) = None
    
    示例:Node.js express-validator

    JavaScript

    const { body } = require('express-validator');
    app.post('/user',[body('name').isString().isLength({ min: 1, max: 100 }),body('email').isEmail(),body('age').optional().isInt({ min: 0, max: 150 })],(req, res) => { ... }
    );

    5. 错误处理最佳实践

    json

    {"error": {"code": "validation_error","message": "输入数据验证失败","details": [{"field": "email","message": "邮箱格式不正确","value": "invalid-email"},{"field": "age","message": "年龄必须大于0","value": -5}],"documentation_url": "https://api.example.com/docs/errors#validation_error"}
    }

    你给的是一个标准错误响应体,讲究结构清晰、可扩展、便于前端解析和自动化处理。

    JSON

    {"error": {"code": "validation_error","message": "输入数据验证失败","details": [{"field": "email","message": "邮箱格式不正确","value": "invalid-email"},{"field": "age","message": "年龄必须大于0","value": -5}],"documentation_url": "https://api.example.com/docs/errors#validation_error"}
    }
    

    1. 结构逐项解释

    • "error":错误信息主对象
      • "code":错误代码,唯一标识类型,便于前端/客户端做自动化处理(如validation_error
      • "message":人类可读的提示,给前端/用户展示
      • "details":详细的错误列表,每个字段的具体问题
        • "field":哪个字段出错
        • "message":该字段的详细错误提
        • "value":实际输入的非法值,方便前端定位和修正
      • "documentation_url":指向API文档的错误说明,便于开发者查阅和快速定位问题

    2. 为什么要这么设计?

    • 前端用户能一眼知道哪里错了、怎么改
    • 自动化客户端(如App、SDK)可以根据code自动处理(比如弹窗、定位输入框)
    • 具体错误列表(details)支持一次返回多个错误,用户不用反复提交修正
    • documentation_url方便开发者自查,降低沟通成本

    3. 实际开发建议

    • 错误代码要有统一规范和枚举,比如validation_errornot_foundunauthorized
    • 错误信息要本地化,支持多语言
    • 错误详情要结构化,方便前端自动处理
    • 文档链接要维护好,便于开发者自助查找
    • 返回HTTP状态码要和错误体对应,比如400 Bad Request对应校验错误

    4. 代码实现示例

    Python FastAPI

    Python

    from fastapi import FastAPI, Request
    from fastapi.responses import JSONResponse
    from fastapi.exceptions import RequestValidationErrorapp = FastAPI()@app.exception_handler(RequestValidationError)
    async def validation_exception_handler(request: Request, exc: RequestValidationError):details = []for err in exc.errors():details.append({"field": err['loc'][-1],"message": err['msg'],"value": err.get('ctx', {}).get('value', None)})return JSONResponse(status_code=400,content={"error": {"code": "validation_error","message": "输入数据验证失败","details": details,"documentation_url": "https://api.example.com/docs/errors#validation_error"}})
    
    Node.js express-validator

    JavaScript

    const { validationResult } = require('express-validator');
    app.post('/user', [...], (req, res) => {const errors = validationResult(req);if (!errors.isEmpty()) {return res.status(400).json({error: {code: "validation_error",message: "输入数据验证失败",details: errors.array().map(e => ({field: e.param,message: e.msg,value: e.value})),documentation_url: "https://api.example.com/docs/errors#validation_error"}});}// 业务逻辑...
    });
    

    三、工程总结

    • 数据验证是API安全和用户体验的基础,建议所有输入都做结构化验证
    • 错误处理要结构清晰、信息丰富、代码标准化,便于前端和开发者自动处理。
    • 推荐用JSON Schema、统一错误码、详细details、文档链接等设计模式,提升API质量。

    公司工作中的实际应用

    1. 微服务架构中的 API 设计

    http

    # 用户服务
    GET /users/{id}# 订单服务  
    GET /orders?user_id={userId}# 支付服务
    POST /payments# 使用 API 网关统一入口
    GET /api/users/{id}
    GET /api/orders
    POST /api/payments

    2. 前后端分离架构

    javascript

    // 前端代码调用 RESTful API
    async function getUserProfile(userId) {try {const response = await fetch(`/api/users/${userId}`, {headers: {'Authorization': `Bearer ${getAuthToken()}`,'Content-Type': 'application/json'}});if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);}const data = await response.json();return data;} catch (error) {console.error('获取用户信息失败:', error);throw error;}
    }

    3. 第三方集成

    http

    # 提供 webhook 支持
    POST /webhooks/order-created
    Content-Type: application/json
    X-Webhook-Signature: sha256=...{"event": "order.created","data": {"order_id": "12345","amount": 99.99,"currency": "USD"}
    }

    面试常见问题及详解

    1. REST 和 RESTful 的区别是什么?

    • REST 是一种架构风格,包含一组约束条件

    • RESTful 是指符合 REST 架构风格的 API 或服务

    • 简单说:REST 是理论,RESTful 是实践

    2. 什么是幂等性?为什么重要?

    • 幂等性:多次执行相同操作产生的结果与执行一次相同

    • 重要原因

      • 网络不稳定时客户端可以安全重试

      • 简化错误处理和恢复机制

    • 幂等方法:GET、PUT、DELETE、HEAD、OPTIONS

    • 非幂等方法:POST、PATCH

    3. PUT 和 PATCH 的区别?

    • PUT:完整更新资源,客户端提供完整资源表述

    • PATCH:部分更新资源,客户端只提供需要修改的字段

    • 示例

      http

      # PUT - 更新整个用户资源
      PUT /users/123
      {"name": "New Name","email": "new@example.com","age": 30
      }# PATCH - 只更新邮箱
      PATCH /users/123
      {"email": "new@example.com"
      }

    4. 如何设计一个好的 RESTful API?

    1. 使用名词而非动词/users 而不是 /getUsers

    2. 使用合适的HTTP方法:GET、POST、PUT、DELETE、PATCH

    3. 使用正确的HTTP状态码:200、201、400、404等

    4. 提供清晰的错误信息:包含错误代码和详细信息

    5. 版本控制:在URI或请求头中包含版本信息

    6. 支持过滤、排序、分页:使用查询参数

    7. 提供HATEOAS支持:响应中包含相关操作链接

    8. 良好的文档:使用OpenAPI等标准

    5. 如何保证 RESTful API 的安全性?

    1. HTTPS:所有通信使用加密连接

    2. 认证:JWT、OAuth 2.0、API密钥等

    3. 授权:基于角色的访问控制(RBAC)

    4. 输入验证:验证所有输入数据,防止注入攻击

    5. 速率限制:防止滥用和DDoS攻击

    6. 日志和监控:记录所有API调用,监控异常行为

    7. CORS配置:正确配置跨域资源共享

    6. 什么是 HATEOAS?为什么重要?

    • HATEOAS:Hypermedia as the Engine of Application State

    • 核心思想:响应中包含相关操作的链接,客户端通过这些链接发现和操作资源

    • 重要性

      • 减少客户端与服务器的耦合

      • 使API更易于发现和使用

      • 支持服务器的演进,不影响现有客户端

    7. 如何处理 API 版本升级?

    1. URI版本控制/api/v1/users/api/v2/users

    2. 请求头版本控制Accept: application/vnd.example.v1+json

    3. 向后兼容:尽量保持向后兼容,不破坏现有客户端

    4. 弃用策略:明确标记已弃用的端点,提供迁移指南

    5. 版本支持周期:明确每个版本的支持时间

    8. REST 和 GraphQL 的区别?

    特性RESTGraphQL
    数据获取多个端点,可能过度获取或获取不足单个端点,精确获取所需数据
    版本控制通过URI或请求头通过Schema演进
    错误处理HTTP状态码总是返回200,错误在响应体中
    实时数据需要WebSocket或轮询支持订阅(Subscriptions)
    学习曲线相对简单需要学习GraphQL语法和类型系统

    9. 如何设计分页?

    http

    # 基于偏移量的分页
    GET /users?page=2&limit=20# 响应
    {"data": [...],"pagination": {"page": 2,"limit": 20,"total": 100,"pages": 5}
    }# 基于游标的分页(更适合大数据集)
    GET /users?cursor=abc123&limit=20# 响应
    {"data": [...],"pagination": {"next_cursor": "def456","has_more": true}
    }

    分页(Pagination)在RESTful API和数据库开发中非常重要,直接影响到性能、用户体验和可扩展性。
    下面我会详细讲解
    分页的概念、两大主流方案(偏移量分页、游标分页)、它们的工作原理、优缺点、响应结构以及实际开发的工程要点
    ,让你彻底理解并能灵活应用。


    一、什么是分页?为什么要分页?

    分页就是把大量数据分成若干页,每次只返回一部分,比如列表、搜索结果、日志等。

    目的:

    • 提升性能:一次只查一页数据,避免一次性拉取全量数据,降低服务器和网络压力。
    • 改善体验:前端/客户端可以“翻页”浏览数据,响应更快。
    • 支持无限滚动/加载更多:比如App里的“下拉加载更多”,都是分页技术。

    二、主流分页设计方案

    1. 基于偏移量分页(Offset Pagination)

    原理

    • API参数中指定pagelimit,服务端根据这两个参数从数据库查第几页、每页多少条。
    • 适合数据总量不是特别巨大的场景。

    请求示例

    HTTP

    GET /users?page=2&limit=20
    
    • page=2:请求第2页
    • limit=20:每页20条

    响应示例

    JSON

    {"data": [ ... ], // 本页的数据"pagination": {"page": 2,"limit": 20,"total": 100,   // 总数据条数"pages": 5      // 总页数(total/limit取整)}
    }
    
    • data:当前页的数据
    • pagination:分页元数据,前端可以据此渲染页码、"上一页/下一页"按钮等

    优点

    • 简单易懂,前后端都好实现
    • 适合中小型数据集、管理后台

    缺点

    • 大数据集性能差,每次都要数据库跳过大量数据(如MySQL的OFFSET),会变慢
    • 数据变动时可能重复或漏掉数据,比如有人插入/删除了数据,页码就会错乱

    2. 基于游标的分页(Cursor Pagination)

    原理

    • 不用page/offset,而是用某个唯一标识(如ID、时间戳)做"游标",每次请求告知服务端从哪继续查。
    • 适合大量数据/实时流场景,比如社交消息流、日志系统。

    请求示例

    HTTP

    GET /users?cursor=abc123&limit=20
    
    • cursor=abc123:上一次请求最后一条数据的游标,下次从这里继续查
    • limit=20:每次查20条

    响应示例

    JSON

    {"data": [ ... ],"pagination": {"next_cursor": "def456",  // 下一页的游标"has_more": true          // 是否还有更多数据}
    }
    
    • next_cursor:下一页继续请求时要传的游标
    • has_more:是否还有未返回的数据

    优点

    • 对大数据、实时流性能好,不需要数据库OFFSET跳过数据
    • 数据插入/删除不会影响分页顺序,不会漏数据/重复数据
    • 适合无限加载/滚动等现代应用场景

    缺点

    • 前端不能直接跳到第N页,只能“下一页/上一页”翻
    • API和前端逻辑稍复杂,需要管理游标

    三、分页响应结构设计要点

    无论哪种分页,响应里都建议带分页元数据

    • 对于偏移量分页:
      • page, limit, total, pages
    • 对于游标分页:
      • next_cursor, has_more

    这样前端可以根据这些信息渲染"更多"按钮、页码导航、判断是否到底等。


    四、实际开发注意事项

    • 偏移量分页适合后台管理、小型列表,比如运营后台、报表、订单列表
    • 游标分页适合大数据/高并发/社交消息流,比如微博、聊天、日志、评论流
    • 游标可以用ID、时间戳、复合键等,确保唯一和顺序
    • 响应里建议带足够元信息,便于前端分页体验优化
    • 分页参数要有默认值和最大值,防止一次拉取太多数据
    • 错误处理要清楚,如游标无效、参数越界等

    五、代码/数据库实现举例

    偏移量分页(SQL举例)

    SQL

    SELECT * FROM users ORDER BY id LIMIT 20 OFFSET 20;  -- 第2页,每页20条
    

    游标分页(SQL举例)

    SQL

    -- 假设cursor是最后一条id
    SELECT * FROM users WHERE id > 123 ORDER BY id LIMIT 20;
    
    • 取到数据后,把最后一条的id作为next_cursor返回给前端

    六、工程总结

    • 分页让API高效、安全、可扩展,是列表型数据必备
    • 偏移量分页简单,适合小数据
    • 游标分页强大,适合大数据和无限滚动
    • 响应格式要有详细的分页信息,前端才能做好的体验

    10. 如何测试 RESTful API?

    1. 单元测试:测试单个端点或功能

    2. 集成测试:测试多个组件的协作

    3. 端到端测试:测试完整流程

    4. 性能测试:测试API的响应时间和吞吐量

    5. 安全测试:测试认证、授权、注入等安全问题

    6. 工具:Postman、curl、Jest、Supertest、LoadRunner等

    总结

    RESTful API 是现代Web开发的核心技术,良好的API设计能够提高系统的可维护性、可扩展性和可用性。在实际工作中,需要综合考虑资源设计、HTTP方法使用、状态码选择、版本管理、安全性等多个方面。在面试中,除了理解基本概念外,还需要能够结合实际场景分析问题和提出解决方案。

    掌握RESTful API不仅需要理论知识,更需要在实际项目中不断实践和总结经验。随着技术的发展,虽然出现了GraphQL等替代方案,但RESTful API仍然是目前最广泛使用的API设计风格,深入理解其原理和最佳实践对于任何Web开发者都是必不可少的技能。

    http://www.dtcms.com/a/392627.html

    相关文章:

  • Linux知识回顾总结----进程间通信(上)
  • Qwen3-Next深度解析:阿里开源“最强性价比“AI模型,如何用3%参数超越全参数模型?
  • AutoResetEvent:C# 线程同步工具
  • ThinkSound - 阿里通义开源的AI音频生成模型
  • Wan2.2-S2V-14B:音频驱动的电影级视频生成模型全方位详解
  • 基于C++11手撸前端Promise——从异步编程到现代C++实践
  • 构建AI智能体:三十九、中文新闻智能分类:K-Means聚类与Qwen主题生成的融合应用
  • [vibe code追踪] 程序列表视图 | renderNodeList
  • 解决 `sudo rosdepc init` 报错:`command not found` 的完整指南
  • 大数据毕业设计选题推荐-基于大数据的气候驱动的疾病传播可视化分析系统-Hadoop-Spark-数据可视化-BigData
  • Maven 实战:多模块项目与高级打包配置
  • AI 精准绘图专栏:从描述到图像,让创意精准落地​
  • 基于C++11手撸前端Promise进阶——链式调用与组合操作(All/Race)的实现
  • 美国批准通用上市标准!加密货币ETF即将爆发?
  • 子查询及其分类
  • MySQL的存储引擎(一条sql语句的执行流程是什么样的?)
  • JavaScript学习笔记(二):遍历方法汇总
  • Ubuntu22.04显卡掉驱动,重装命令
  • 模式组合应用-享元模式
  • 租房小程序房产小程序源码方案详解
  • p-value与e-value
  • 面经分享--京东一面
  • 大数据毕业设计选题推荐-基于大数据的帕金森病数据可视化分析系统-Spark-Hadoop-Bigdata
  • stack 和 queue
  • 执行yarn init报错:error Invalid package name.(question name)包名格式不对
  • Windows 下 PyTorch 入门深度学习环境安装与配置 CPU GPU 版 | 土堆教程
  • Transformer中为什么要使用多头注意力?
  • 《嵌入式硬件(十六):基于IMX6ULL的I2C的操作》
  • AI.工作助手.工作提效率
  • 【开题答辩全过程】以 Louis宠物商城为例,包含答辩的问题和答案