HTTP RESTful规范说明

1. API的就是程序员的UI,和其他UI一样,必须仔细考虑它的用户体验!

几个必须的原则:

  1. 当标准合理的时候遵守标准。
  2. API应该对程序员友好,并且在浏览器地址栏容易输入。
  3. API应该简单,直观,容易使用的同时优雅。
  4. API应该具有足够的灵活性来支持上层ui。
  5. API设计权衡上述几个原则。

2. URL设计

REST的核心原则是将你的API拆分为逻辑上的资源。这些资源通过http被操作(GET ,POST,PUT,DELETE). 显然从API用户的角度来看,”资源“应该是个名词。即使你的内部数据模型和资源已经有了很好的对应,API设计的时候你仍然不需要把它们一对一的都暴露出来。这里的关键是隐藏内部资源,暴露必需的外部资源。

URL遵循动词+宾语的结构

2.1 动词
请求含义
GET读取/Read
POST新建/Create
PUT更新/Update
PATCH部分更新/Update
DELETE删除/Delete
2.2 宾语

宾语必须为名词且最好为复数,同时要避免多级URL,可通过参数来展示分类. 使用复数使得URL更加规整, 这让API使用者更加容易理解,对开发者来说也更容易实现.

一旦定义好了要暴露的资源,你可以定义资源上允许的操作,以及这些操作和你的API的对应关系:

  • GET /tickets           # 获取所有ticket列表
  • GET /tickets/12            # 查看某个具体的ticket(id为12)
  • POST /tickets               # 新建一个ticket
  • PUT /tickets/12            # 更新ticket(id为12)
  • DELETE /tickets/12     # 删除ticekt(id为12)

可以看出使用REST的好处在于可以充分利用http的强大实现对资源的CURD功能。而这里只需要一个endpoint:/tickets, 再没有其他什么命名规则和url规则了.

2.3 处理资源关联

如何处理关联?关于如何处理资源之间的管理REST原则也有相关的描述:

  • GET /tickets/12/messages               # 获取ticket(id为12)的所有messages列表
  • GET /tickets/12/messages/5           # 获取ticket(id为12)的某个具体message(id为5)
  • POST /tickets/12/messages            # 为ticket(id为12)新建一个message
  • PUT /tickets/12/messages/5           # 更新ticket(id为12)的message(id为5)
  • PATCH /tickets/12/messages/5      # 部分更新ticket(id为12)的message(id为5)
  • DELETE /tickets/12/messages/5    # 删除ticket(id为12)的message(id为5)

其中,如果这种关联和资源独立,那么可以在资源的输出表示中保存相应资源的endpoint。然后API的使用者就可以通过点击链接找到相关的资源。如果关联和资源联系紧密。资源的输出表示就应该直接保存相应资源信息。(例如这里如果message资源是独立存在的,那么上面 GET /tickets/12/messages就会返回相应message的链接;相反的如果message不独立存在,他和ticket依附存在,则上面的API调用返回直接返回message信息)

2.4 版本化

在API上加入版本信息可以有效的防止用户访问已经更新了的API,同时也能让不同主要版本之间平稳过渡。

版本化API的通常方式有:

2.4.1 URI中设置版本

这种方式通常在URI中增加一段用于标识版本,例如/v1/v2等。例如:

curl https://example.com/api/v2/lists/3

这种方式的优势在于版本信息很容易明显的看出来,可以通过浏览器直接访问。

2.4.2 HTTP头中设置版本

这种方式的版本信息会放在HTTP的请求头中,通常会利用Accept字段,或者自定义一个字段。例如:

curl https://example.com/api/lists/3 \  
-H 'Accept: application/vnd.example.v2+json'

这种方式的好处是当版本升级时,URI保持不变,并且仅用于表示资源定位。

2.4.3 没有版本

版本化的目的是为了标识API的变化,如果API不会变化,或者每次都会重新扩展新的API,这种情况下,就可以标识版本信息。例如:

curl https://example.com/api/lists/3
2.4.4 一种折中方案

前面提到了三种版本化API的方式,通常情况下需要针对自己业务的特殊性来挑选其中的一种方式。但是,在实际应用场景中,情况会更加复杂,API的升级通常有两种情况:

  1. 大版本更新,例如字段类型变更、数据对象变更等。这种情况下无法满足对客户端的向下兼容,因此需要修改版本号。
  2. 小版本更新,例如增加可选参数、增加返回字段等。这种情况对于新客户端可以增加功能,对于老客户端仍然可以保持原有功能,可以不修改版本号。

因此,折中方案是基于URI中的大版本号和HTTP头中的小版本号整合的方式。下面通过一个简单的示例来解释。

用户管理平台

一个常用的用户管理平台,提供以下API,通过用户ID获取用户信息:

curl https://example.com/api/v1/user/1
...
{
  "id": 1,
  "name": "test",
  "email": "test@example.com"
}

考虑以下两种变动情况:一种是用户id从数字变成了字符串,另一种是新增一个用户头像的值。

前者修改因为数据类型的变化,会导致客户端解析出现问题。因此这样的修改已经破坏了向下兼容性,此时就需要修改API的版本号。例如:

curl https://example.com/api/v2/user/1
...
{
  "id": "1",
  "name": "test",
  "email": "test@example.com"
}

第二种情况,对于旧客户端来说,只是增加了不使用的字段,通常的JSON格式解析库都可以忽略这些不使用的字段。对于新客户端则可以读取新的字段。例如:

curl https://example.com/api/v2/user/1
...
{
  "id": "1",
  "name": "test",
  "email": "test@example.com",
  "avatar": "http://example.com/1.jpg"
}

这种情况下,基本可以做到向下兼容,因此可以算是“小版本升级”。针对小版本升级,可以将小版本号放到HTTP头中。例如:

curl https://example.com/api/v2/user/1 \
    -H 'API-VERSION: 20170801'
...
{
  "id": "1",
  "name": "test",
  "email": "test@example.com",
  "avatar": "http://example.com/1.jpg"
}
2.4.5 后端路由

由于混合版本化的方式同时涉及到URI和HTTP头字段,前端代理(例如HAProxy、nginx)可以通过这些特定版本号字段将请求代理到对应的后端应用。

例如,前端使用HAProxy进行多版本分发,可以针对URI和HTTP头定制acl,然后再对这些acl进行组合,设置不同的backend。

acl is_v1 path_beg /api/v1
acl is_v2 path_beg /api/v2
acl is_version_1 hdr(API-VERSION) 20170801
acl is_version_2 hdr(API-VERSION) 20170701
use_backend old_server if is_v1 is_version_1
use_backend new_server if is_v2 is_version_2
backend old_server
...
backend new_server
...

这样可以将API版本化规则应用到不同的后端,以保证向下兼容性。

2.4.6 总结

基于版本化API规则,将“大版本”应用在URI上,将“小版本”应用在HTTP头字段上。通常来说,如果API升级之后破坏了向下兼容性,就应该升级“大版本”号;如果API升级可以向下兼容,可以升级“小版本”号。

版本化API有很多不同的设计方式,实际应用时,还是要根据业务场景进行选择,包括API版本升级频率,API稳定性等。通过HAProxy、nginx等代理服务,可以在确保向下兼容的情况下,由业务方决定老版本API的保留时间。

URL设计

URL遵循动词+宾语的结构

3.状态码

1xx 相关信息

API不需要1xx状态码

2xx 操作成功
状态码含义
200 OK成功
201 Created生成了新的资源
202 Accepted已经收到请求但还未处理完成/异步操作
204 No Content(删除时)资源已不存在
3xx 重定向
状态码含义
301 Moved Permanently永久重定向/资源的URL已被更新-API用不到
302暂时重定向/用于GET请求-API用不到
303 See Other暂时重定向/用于POST/PUT/DELETE请求
304 Not Modified资源未更改/缓存
307暂时重定向/用于GET请求-API用不到
4xx 客户端错误
状态码含义
400 Bad Request服务器不理解客户端的请求,未做任何处理
401 Unauthorized用户未提供身份验证凭据,或者没有通过身份验证
403 Forbidden用户通过了身份验证,但是不具有访问资源所需的权限
404 Not Found所请求的资源不存在,或不可用
405 Method Not Allowed用户已经通过身份验证,但是所用的 HTTP 方法不在他的权限之内
406 Not Acceptable服务端不支持所需表示/参数格式错误
409 Conflict通用冲突
410 Gone所请求的资源已从这个地址转移,不再可用
412 Precondition Failed前置条件失败(如执行条件更新时的冲突)
415 Unsupported Media Type客户端要求的返回格式不支持
422 Unprocessable Entity客户端上传的附件无法处理,导致请求失败
429 Too Many Requests客户端的请求次数超过限额
5xx 服务端错误
状态码含义
500 Internal Server Error客户端请求有效,服务器处理时发生了意外
503 Service Unavailable服务器无法处理请求,一般用于网站维护状态

4.服务器回应

API 返回的数据格式,不应该是纯文本,而应该是一个 JSON 对象,因为这样才能返回标准的结构化数据。所以服务端返回值的Content-Type属性应为application/json,同时客户端请求头ACCEPT属性应为application/json。

发生错误时,不要返回200状态码,这样相当于状态码失去了意义。应该用状态码表示发生的错误,然后在信息里写明错误详情

提供链接(这个看不懂)

应用范例

this.Ctx.ResponseWriter.WriteHeader(http.StatusOK)
//手动控制HTTP状态码
this.Ctx.ResponseWriter.Write([]byte("byebye"))
//手动控制HTTP返回值
beego.Router("/user/:id([0-9]+",&controllers.UserController{},"GET:Get,PUT:Put,DELETE:Delete")
//同个接口路径不同类型访问导向不同方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值