3. API设计原则:RESTful风格、无状态、幂等性、版本控制

商户风险评分API作为金融级接口,其设计必须遵循一套严格的原则,以确保系统的可维护性、可靠性与可扩展性。本章将深入剖析四项核心设计原则:RESTful风格、无状态、幂等性与版本控制。这些原则共同构成了API架构的基石,直接影响着商户接入的体验与风控系统的稳定性。

3.1 RESTful风格:资源导向的架构

RESTful风格将API视为对“资源”的操作,而非对“动作”的调用。在商户风险评分场景中,资源包括“商户”、“评分记录”、“风险事件”等。通过标准的HTTP方法(GET、POST、PUT、PATCH、DELETE)对资源进行增删改查,使接口语义清晰、易于理解。

核心设计要点:
  • 资源路径(Endpoint):使用名词复数形式,如 /api/v3/merchants/{merchantId}/risk-scores。
  • HTTP方法语义化:
    • GET:查询资源(如获取商户评分)。
    • POST:创建资源(如提交新的评分请求)。
    • PUT:全量更新资源(如覆盖商户配置)。
    • PATCH:部分更新资源(如更新评分状态)。
    • DELETE:删除资源(如移除过期记录)。
  • 状态码标准化:使用HTTP状态码表达结果,如 200 OK、201 Created、400 Bad Request、404 Not Found。
RESTful 资源操作流程 客户端 资源层 /merchants/{id} /risk-scores/{id} 数据存储 GET/POST/PUT CRUD 操作 HTTP 状态码 + JSON 响应 💡 设计提示: 避免在路径中使用动词(如 /getMerchantScore),应使用资源路径 + HTTP 方法表达语义。 例如:POST /api/v3/merchants/{id}/risk-scores 表示“创建评分”。

3.2 无状态:水平扩展的基石

无状态原则要求每个API请求都包含服务端处理所需的所有信息,服务端不存储任何客户端会话状态。在风控场景中,这意味着每次评分请求都必须携带完整的商户标识、认证令牌和上下文参数,服务端不依赖之前的请求记录。

特性 有状态设计 无状态设计(推荐)
会话存储 服务端保存 session 客户端携带 token
扩展性 需会话黏性,扩展困难 任意节点处理,水平扩展
故障恢复 会话丢失导致失败 请求可重试到其他节点
风控场景 依赖上下文,易出错 每次请求独立,可审计
实践建议:使用JWT(JSON Web Token)承载认证信息与必要上下文。每次评分请求都应包含 Authorization: Bearer <token> 头,以及 merchant_id、transaction_id 等业务参数,确保请求自包含。

3.3 幂等性:防止重复处理的保障

幂等性是指无论请求执行一次还是多次,对资源产生的最终影响相同。在商户风险评分中,幂等性至关重要——网络重试、超时重发等场景下,必须防止重复评分导致的风控结果错误或商户数据污染。

⚠️ 关键风险:如果评分接口不具备幂等性,一次网络超时后的重试可能导致同一笔交易被重复评分,产生两条不同的评分记录,进而触发错误的风控决策(如重复拦截或重复放行)。

幂等性实现策略

  • 幂等键(Idempotency-Key):客户端为每个请求生成唯一键(如UUID),服务端记录已处理的键,重复请求直接返回首次结果。
  • 自然幂等操作:
    • GET、PUT、DELETE 方法天然幂等。
    • POST 方法需额外实现幂等机制。
  • 业务去重:利用业务唯一标识(如 transaction_id)进行去重,确保同一笔交易只被评分一次。
// 幂等键使用示例(HTTP 请求头)
POST /api/v3/merchants/123/risk-scores
Content-Type: application/json
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000

{
  "transaction_id": "TXN20250321001",
  "amount": 1500.00,
  "currency": "CNY"
}

// 服务端响应(首次)
HTTP/1.1 201 Created
{
  "score_id": "SCR-20250321-001",
  "risk_level": "medium",
  "score": 65
}

// 重复请求(相同幂等键)
HTTP/1.1 200 OK  // 返回已存在的记录,而非新建
{
  "score_id": "SCR-20250321-001",
  "risk_level": "medium",
  "score": 65
}
幂等性处理流程 客户端 请求1(Key: A) 幂等检查器 Key 是否存在? 存储:{Key: 响应} 不存在 → 执行 执行业务逻辑 存储 Key + 响应 返回 201 Created 请求2(Key: A)重试 存在 → 直接返回 返回已存储响应 💡 幂等键有效期建议设置为 24 小时,过期后允许相同 Key 再次创建新资源。

3.4 版本控制:平滑演进的保障

API版本控制允许在引入新功能或修改现有行为时,不影响已接入的商户。版本控制策略直接决定了风控系统的迭代效率与商户的迁移成本。

版本控制策略对比

策略 实现方式 优点 缺点
URI路径版本 /api/v3/merchants/... 直观、易于路由 URL 变更,需修改客户端
请求头版本 Accept: application/vnd.risk.v3+json URL 不变,灵活 调试不便,需解析头
查询参数版本 /api/merchants?version=3 实现简单 缓存友好性差
推荐方案:采用 URI路径版本 作为主策略,版本号使用整数(v1、v2、v3),避免语义化版本(如 v3.1.2)。同时支持 请求头版本 作为辅助,用于灰度发布和内部测试。

版本生命周期管理

  • 主版本(Major):不兼容的API变更,如删除字段、修改请求结构。至少提前 6 个月通知商户。
  • 次版本(Minor):向后兼容的新增功能,如增加可选字段。无需通知商户。
  • 补丁(Patch):内部修复,不影响接口行为。无需版本变更。
  • 废弃策略:旧版本标记为“废弃(Deprecated)”,保留至少 12 个月后下线。
// 版本控制示例
// v2 接口(旧版本,已废弃)
GET /api/v2/merchants/123/risk-score
Response: { "score": 65, "level": "medium" }

// v3 接口(新版本,推荐使用)
GET /api/v3/merchants/123/risk-scores
Response: {
  "score_id": "SCR-20250321-001",
  "score": 65,
  "risk_level": "medium",
  "factors": [
    { "name": "transaction_velocity", "weight": 0.3, "score": 70 },
    { "name": "amount_anomaly", "weight": 0.4, "score": 60 }
  ]
}

// 响应头中声明版本
HTTP/1.1 200 OK
Content-Type: application/json
API-Version: v3
Deprecated: true  // 仅 v2 响应包含此头
Sunset: Sat, 31 Mar 2026 23:59:59 GMT  // 下线时间
最佳实践:在响应头中添加 API-Version 字段,方便商户确认当前使用的版本。对于废弃版本,添加 Deprecated 和 Sunset 头,明确告知商户迁移时间线。