3、RESTful通信:RESTful API设计原则、HTTP方法在Adaptive中的应用、资源表示与状态码

好,咱们进入第三章。RESTful通信,说白了就是咱们Adaptive平台跟外界打交道的那张嘴。你想想看,一个通信系统再牛,如果跟外部系统说不上话,那跟废铁有什么区别?我个人习惯,在设计API之前,先想清楚三个问题:谁在调用?调用来干嘛?返回什么?想明白了,设计起来就顺了。

3.1 RESTful API设计原则——别把简单事情搞复杂

RESTful设计,核心就六个字:资源、动作、表述。我在项目中遇到过不少团队,一上来就搞什么「/getUserInfo」、「/deleteOrder」这种RPC风格的接口。嗯,这其实违背了REST的初衷。REST是把一切都看作资源,用HTTP方法来表达操作。

几个关键原则,我列一下:

  • 资源导向:URL只表示资源,不表示动作。比如 /users 表示用户集合,/users/123 表示某个用户。
  • 无状态:每个请求都包含所有必要信息,服务器不保存客户端状态。说白了,这次请求跟上次没关系。
  • 统一接口:用标准的HTTP方法(GET、POST、PUT、DELETE等)来操作资源。
  • 超媒体驱动:响应里带上链接,告诉客户端下一步能做什么。这个在Adaptive里用得不多,但了解没坏处。

核心要点:RESTful API的URL应该是名词复数形式,用斜杠表示层级关系,用查询参数做过滤和分页。千万别在URL里出现动词。

举个例子,咱们Adaptive平台里有个设备管理模块。错误的做法是 /getDeviceStatus?id=5,正确的做法是 /devices/5/status。你看,一目了然。

3.2 HTTP方法在Adaptive中的应用——每个动词都有它的脾气

HTTP方法,说白了就是告诉服务器你想干嘛。Adaptive平台里,我主要用这么几个:

HTTP方法 用途 Adaptive中的典型场景 幂等性
GET 获取资源 查询设备列表、获取配置
POST 创建资源 注册新设备、提交告警
PUT 全量更新 更新设备配置(整体替换)
PATCH 部分更新 修改设备某个参数
DELETE 删除资源 移除设备、清除日志

这里有个坑,我踩过。POST和PUT的区别,很多人搞混。POST是不幂等的,你发两次可能创建两个资源。PUT是幂等的,发多少次结果都一样。我曾经在项目里用PUT做创建,结果客户端重试时生成了重复数据,排查了半天才发现问题。

避坑指南:我曾经在Adaptive的告警接口里,用POST来更新告警状态。后来发现告警被重复处理了。正确的做法是:创建用POST,全量更新用PUT,部分更新用PATCH。别混着用。

再补充一点,Adaptive平台里有个特殊场景——批量操作。比如批量注册设备,我建议用POST到集合资源,请求体里放一个数组。别搞什么 /batchCreateDevices,那又回到RPC的老路上了。

3.3 资源表示与状态码——你给出去的,决定了别人怎么用

资源表示,就是服务器返回的数据长什么样。Adaptive平台里,我统一用JSON格式。为什么?轻量、易读、解析快。XML?嗯,除非有特殊要求,否则别碰。

一个标准的资源表示,应该包含三部分:

  • 数据本身:比如设备ID、名称、状态等
  • 链接:关联资源的URL,方便客户端导航
  • 元数据:比如创建时间、更新时间、版本号等

举个例子,获取某个设备的响应:

{
  "id": "dev-001",
  "name": "基站A-3号",
  "status": "online",
  "location": "机房B-2",
  "created_at": "2024-01-15T08:30:00Z",
  "updated_at": "2024-03-20T14:22:00Z",
  "_links": {
    "self": { "href": "/devices/dev-001" },
    "metrics": { "href": "/devices/dev-001/metrics" },
    "alarms": { "href": "/devices/dev-001/alarms" }
  }
}

你看,我习惯在响应里带上 _links 字段。这样客户端拿到数据后,不用硬编码URL,直接跟着链接走就行。这在Adaptive的微服务架构里特别有用,服务地址变了也不影响客户端。

小技巧:我个人习惯在响应里加一个 version 字段,比如 "version": "1.0"。这样接口升级时,客户端可以根据版本号做兼容处理。别问我为什么,问就是被坑过。

再说状态码。HTTP状态码不是随便选的,每个数字都有它的含义。我见过有人不管成功失败都返回200,然后在响应体里放一个 code 字段表示业务状态。嗯,这其实是在绕开HTTP协议,不推荐。

Adaptive平台里,我常用的状态码:

状态码 含义 使用场景
200 OK 请求成功 GET、PUT、PATCH成功
201 Created 资源创建成功 POST成功创建资源
204 No Content 请求成功,无返回体 DELETE成功
400 Bad Request 请求参数错误 缺少必填字段、格式错误
401 Unauthorized 未认证 缺少或无效的认证令牌
403 Forbidden 无权限 认证通过但权限不足
404 Not Found 资源不存在 请求的资源ID无效
409 Conflict 资源冲突 创建重复资源、版本冲突
422 Unprocessable Entity 语义错误 业务逻辑校验失败
500 Internal Server Error 服务器内部错误 未捕获的异常

这里有个细节要注意。404和403的区别,很多人搞混。404是「找不到」,403是「不让看」。你想想看,如果用户没权限访问某个设备,你返回404,他以为设备不存在。返回403,他才知道是权限问题。我建议统一用403,别藏着掖着。

经验之谈:错误响应里,除了状态码,还要带上错误信息。我习惯用这样的格式:{"error": {"code": "DEVICE_NOT_FOUND", "message": "设备 dev-999 不存在"}}。这样客户端拿到后,可以直接展示给用户,或者做本地化处理。

最后说一句,RESTful设计没有银弹。Adaptive平台里,有些场景可能不适合纯REST,比如实时数据推送、长轮询等。这时候该用WebSocket就用WebSocket,别死磕REST。设计API,最终目的是让调用方用得舒服,不是炫技。

好,这一章就到这儿。下一章咱们聊聊消息队列在Adaptive里的应用,那可是异步通信的利器。