3. CoAP请求/响应方法与状态码
CoAP 把 HTTP 的“方法 + 状态码”语义压缩到 UDP 之上,但保留了几乎一致的资源操作模型。理解方法语义与响应码分层,是设计设备资源、编写客户端逻辑和排查通信故障的基础。本节先厘清四种方法的幂等性与副作用,再说明 2.xx、4.xx、5.xx 三类响应码的判断边界,最后给出一个可直接对照实现的设备资源操作示例。
一、四种方法的语义与幂等性
CoAP 请求方法定义在 RFC 7252 中,语义与 HTTP 对齐,但作用对象是 CoAP 资源(URI 路径)。判断方法是否可安全重传,关键看两点:是否改变资源状态(副作用),以及重复执行是否产生相同结果(幂等性)。
| 方法 | 语义 | 副作用 | 幂等 | 典型设备用途 |
|---|---|---|---|---|
| GET | 读取资源当前表示 | 无 | 是 | 读温度、读开关状态、读版本号 |
| POST | 向资源提交数据,由服务端决定处理结果 | 有 | 否 | 触发动作、追加日志、提交控制指令 |
| PUT | 用请求负载整体替换目标资源 | 有 | 是 | 写入配置、设置目标温度、覆盖阈值 |
| DELETE | 删除目标资源 | 有 | 是 | 删除定时任务、注销订阅、清除告警记录 |
幂等性直接决定重传策略。CoAP 运行在 UDP 上,报文可能丢失或重复。GET、PUT、DELETE 即使被重复送达,结果与送达一次相同,因此客户端在 CON 消息超时后可以安全重传;POST 不幂等,重复送达可能触发两次动作,例如“开阀”指令被下发两次。对 POST 类操作,设备端应结合 Token 或业务序号做去重,客户端也应避免盲目重传。
二、响应码分层:2.xx、4.xx、5.xx
CoAP 响应码为 8 位,高 3 位是类别(class),低 5 位是细节(detail),书写为 “c.dd”。类别决定客户端应采取的动作方向:
- 2.xx 成功:请求已被理解、接受并处理。常见 2.01 Created(PUT/POST 新建资源成功)、2.02 Deleted(DELETE 成功)、2.04 Changed(PUT/POST 修改成功)、2.05 Content(GET 成功并携带负载)。
- 4.xx 客户端错误:请求本身有问题,重发同样的请求不会成功。常见 4.00 Bad Request(负载格式错误)、4.01 Unauthorized(未通过认证)、4.03 Forbidden(无权限)、4.04 Not Found(资源不存在)、4.05 Method Not Allowed(该资源不支持此方法)、4.15 Unsupported Content-Format(负载媒体类型不支持)。
- 5.xx 服务端错误:请求格式正确,但服务端当前无法完成。常见 5.00 Internal Server Error、5.01 Not Implemented(功能未实现)、5.03 Service Unavailable(设备忙或过载)。
区分 4.xx 与 5.xx 的实践意义在于重试决策:4.xx 应修正请求(改路径、改负载、补认证),重试无意义;5.03 属于暂时性故障,客户端可退避后重试。设备端返回 5.03 时通常还应在响应中携带 Max-Age,提示客户端多久后再来。
三、设备资源操作示例
假设一台环境控制器暴露三个资源:/sensor/temp(只读温度)、/config/target(可读写目标温度)、/tasks/reboot(动作型资源)。下面给出一次完整的操作序列,包含请求方法、负载与预期响应码。
# 1. 读取当前温度(GET,幂等)
GET coap://[fd00::1]/sensor/temp
→ 2.05 Content Payload: {"v":23.5,"u":"C"}
# 2. 修改目标温度(PUT,整体替换,幂等)
PUT coap://[fd00::1]/config/target
Content-Format: application/json
Payload: {"target":25.0}
→ 2.04 Changed
# 3. 触发重启(POST,非幂等,需去重)
POST coap://[fd00::1]/tasks/reboot
Content-Format: application/json
Payload: {"delay":5}
→ 2.01 Created Location-Path: /tasks/reboot/17
# 4. 删除已完成的定时任务(DELETE,幂等)
DELETE coap://[fd00::1]/tasks/reboot/17
→ 2.02 Deleted
# 5. 错误示例:向只读资源写入
PUT coap://[fd00::1]/sensor/temp
Payload: {"v":99}
→ 4.05 Method Not Allowed
# 6. 错误示例:设备过载
POST coap://[fd00::1]/tasks/reboot
→ 5.03 Service Unavailable Max-Age: 10
从示例可以看到几个设计要点。第一,/sensor/temp 只支持 GET,对 PUT 返回 4.05 而不是 4.00,因为请求格式本身没错,是方法不被该资源允许。第二,POST 创建任务后返回 2.01 并带 Location-Path,客户端据此获得新资源路径,后续可用 DELETE 清理。第三,5.03 携带 Max-Age: 10,客户端应在约 10 秒后重试,而不是立即重发造成更大压力。
四、方法选择与资源建模的对应关系
资源设计应让方法语义自然成立。可读状态用 GET;可整体覆盖的配置用 PUT;需要服务端生成标识、追加记录或触发不可重复动作的,用 POST;可撤销的实体用 DELETE。若一个资源同时承担“读状态”和“触发动作”,建议拆成两个 URI,例如 /sensor/temp 与 /tasks/calibrate,避免 GET 与 POST 语义在同一路径上冲突。
响应码的选择同样要克制:能用 2.04 表达修改成功,就不要一律返回 2.05;能区分 4.00 与 4.15,就不要把所有负载问题都归为 4.00。清晰的响应码分层能让客户端用少量分支完成正确决策,也便于抓包时快速定位问题发生在请求侧还是设备侧。