移动端接口指南

该文档参考 REST Web API 设计,但是考虑到目前服务器所使用技术完全使用 REST API 工作量过大。所以做了一些妥协。

加星号部分可根据需求选择性实现。

开始

首先描述一下 Request 和 Response 的大概格式

Request

POST /api HTTP/1.1
Host: www.domain.com
Accept: application/vnd.company+json
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
X-Api-Version: 1.0
User-Agent: app/1.0 CFNetwork/760.6.3 iOS/9.3
Connection: keep-alive
Content-type: application/json
Content-Length: 1024
Etag: "qwghu2u3423kjgh234iughjkh"
X-UUID: 01234567-89ab-cdef-0123-456789abcdef
X-Geo-Location: +40.6894-074.0447

content

Response

HTTP/1.1 200 OK
Date: Thu, 28 Jul 2016 06:55:31 GMT
Content-Type: application/vnd.company+json;charset=utf-8
Vary: Accept-Encoding
Server: nginx/1.0.4
Content-Encoding: gzip
Etag: "qwghu2u3423kjgh234iughjkh"
Content-Length: 1024

{"code":200,"message":"success","data":{"color":"WHITE","size":7.5}}

请求方式

这里分为两种方案:

REST

该方案是 REST API 的请求模式

  • GET (选择):从服务器上获取一个具体的资源或者一个资源列表。
  • POST (创建): 在服务器上创建一个新的资源。
  • PUT(更新):以整体的方式更新服务器上的一个资源。
  • PATCH (更新):只更新服务器上一个资源的一个属性。
  • DELETE(删除):删除服务器上的一个资源。
  • HEAD : 获取一个资源的元数据,如数据的哈希值或最后的更新时间。
  • OPTIONS:获取客户端能对资源做什么操作的信息。

ONLY GET/POST

对于大部分服务器并没有很好的支持 PUT/DELETE 之类的方法,因此,只使用 GET/POST 方法,是个便捷省力的方式。

当然我们也可以把其它操作放到 Request Header 里面,只需要在 Header 里面加上 x-Method 字段就可以了。

说明

对于提交 body 中的数据,服务器只接受 JSON序列化 的数据,不接受 form-encoded 数据,这样可以保持请求和返回都是JSON数据。

注意各方法必须符合下表中 幂等 以及 安全 要求

HTTP 幂等 安全
OPTIONS yes yes
GET yes yes
HEAD yes yes
PUT yes no
POST no no
DELETE yes no
PATCH no no

安全:不改变服务器资源状态
幂等:请求的结果和请求次数无关

PUT 必须是对服务器资源的完整替换,而 PATCH 可以要求服务器执行逻辑操作(比如加减),所以 PATCH 不是幂等的。

X-Api-Version

版本号通常有两种标识方式

一种是放到 header 里,如上面的:

X-Api-Version: 3.0

另一种是直接在URL 添加版本号

http://api.domain.com/v3/

第一种可以针对单个接口设置版本号,第二种是整套接口系统采用统一版本号。

第二种可用于对外开放 API,比如微博、github的 open API,使用统一版本号。

针对单个 APP 通常采用第一种。

自定义 content-type

不要使用标准的 content-type ,因为客户端没有处理这些 content-type 的能力,可以使用下面的自定义格式

Content-type: application/vnd.company+xml
Content-type: application/vnd.company+json

客户端可以根据这些特定的 content-type 写专门的解析方式。

所有 application/vnd 都是提供商自定的 content-type 而不是标准。仅应用于非标准 content-type。

User-Agent

User-Agent 采用 APP名称/版本号 网络组件名/版本号 系统名/版本号 的形式。

x-UUID*

这里发送设备的 UUID,方便服务器进行统计

UUID 使用 01234567-89ab-cdef-0123-456789abcdef 这种形式

Etag*

方便客户端进行数据缓存,后面有详细说明。

x-Geo-Location*

该字段是发送用户当前地理坐标的,同样是方便服务器进行统计的。

当然,部分接口可能也会利用这个字段进行针对性的给用户推送地域性的信息,比如本地新闻

BODY

JSON 数据

{
"code":
"message":
"errors":
"data":
}
  • 严格数据类型
  • 去除空值,除非十分有必要存在,那么则使用 null
  • 时间采用 ISO 8601 标准格式 YYYY-MM-DDTHH:MM:SSZ
  • 只有成功时才返回 data,不成功则不返回 data。data 的数据类型限定为对象或数组
  • 数据中的枚举采用字符枚举如:{"color": "WHITE"}
  • 服务器端可以把所有的错误异常都映射为 error 模板
{
"code" : 422,
"message" : "Unprocessable Entity",
"errors" : [
{
"field" : "first_name",
"message" : "First name cannot have fancy characters"
},
{
"field" : "password",
"message" : "Password cannot be blank"
}
]
}

参考:Google JSON风格指南

分页

推荐使用下列属性对数据进行分页:

  • page 当前页码
  • page_size 每页返回多少数据,可以通过参数指定,但是服务器需设置上限
  • total_page 总页数
  • has_next 是否有下一页

total_page 与 has_next 可以二选一

OTHERS

身份认证

  • basic 认证,只有部署 SSL 证书情况下使用,不推荐
  • token:header 增加 Authorization: Bearer {token} 或者 x-Token 验证
  • OAuth

命名

URL 以及 HTTP Header 中的关键字推荐使用短横线做连接符,例如:

不推荐: http://domain.com/api/book_list
推 荐: http://domain.com/api/book-list

参数(URL Param 和 JSON Key)推荐使用下划线或驼峰式命名,如:

http://domain.com/api/book?book_name=java
http://domain.com/api/book?bookName=java

时间 & 经纬度

时间格式遵循 ISO 8601 建议的格式:

  • 日期: 2014-07-09
  • 时间: 14:31:22+0800
  • 具体时间: 2007-11-06T16:34:41Z
  • 持续时间: P1Y3M5DT6H7M30S (表示在一年三个月五天六小时七分三十秒内)
  • 时间区间: 2007-03-01T13:00:00Z/2008-05-11T15:30:00Z2007-03-01T13:00:00Z/P1Y2M10DT2H30M
  • 重复时间: R3/2004-05-06T13:00:00+08/P0Y6M5DT3H0M0S (表示从2004年5月6日北京时间下午1点起,在半年零5天3小时内,重复3次)

时区推荐默认使用 UTC 时间

纬度/经度应该使用 ISO 6709 建议的格式

{
// 自由女神像的纬度/经度位置.
"statueOfLiberty": "+40.6894-074.0447"
}

安全

使用 SSL,禁用 80 端口访问。

缓存

缓存是客户端的一个重要功能,可以有效减少用户流量,比较推荐的方法是客户端实现 Cache-Control 的缓存逻辑,然后可以根据服务器返回来实现对应的缓存策略。

Cache-Control 通常有下面几种取值:

  • private: 仅用于缓存用户个人资料,其他用户无法访问,且只有第一次会发送请求,以后直接从缓存读取。
  • no-cache: 客户端 request 会携带 Last-ModifiedEtag 询问服务器资源是否更改,如果未更改,则使用缓存。
  • max-age: max-age=60 则表示缓存60秒内有效
  • must-revalidate: 除非用户强制刷新,否则使用缓存数据

Last-Modified

客户端第一次请求时,服务器端的返回 HTTP 200,同时携带 Last-Modified 标记该资源在服务器端最后被修改的时间

Last-Modified:Tue, 24 Feb 2009 08:01:04 GMT

客户端第二次该资源时,向服务器传送 If-Modified-Since 询问该时间之后文件是否有被修改过:

If-Modified-Since:Tue, 24 Feb 2009 08:01:04 GMT

如果服务器端的资源没有变化,则自动返回 HTTP 304 状态码,内容为空。当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。

Etag

首次请求服务器返回资源,header 中包含 Etag,http code 200

Etag: “qwghu2u3423kjgh234iughjkh”

客户端请求同一个文件,header 附加 If-None-Match 字段为服务器返回的 Etag

If-None-Match:“qwghu2u3423kjgh234iughjkh″

服务器检查完修改时间和Etag之后,若资源未改变,则返回 http code 304;否则返回 HTTP 200 并附带新的 Etag。

Expires or max-age

服务器也可以在 Response Header 中附加 Expires 字段,表明资源有效期,但是 Expires 存在服务器时间和客户端时间不同步的问题,所以还是推荐使用 max-age。

max-age 可以和 Etag 或 Last-Modified 配合使用。max-age 有效期内,不访问网络,超出有效期则访问服务器询问资源是否被修改。

参考:
max-age
初探 HTTP 1.1 Cache 機制
浏览器缓存详解:expires,cache-control,last-modified,etag详细说明

错误码

服务器目前只有200,500错误。其它错误均在 Response Body 的 code 字段中返回。

尽管错误码在 Response Body 中,但是我们依然遵循 http status code 对错误码范围的划分,主要考虑就是以后如果使用 Http status code 返回的方式时,目前定义的错误码依然可以使用。

具体为:

  • 200 段表示请求成功

    • 200 OK : 请求执行成功并返回相应数据,如 GET 成功
    • 201 Created : 对象创建成功并返回相应资源数据,如 POST 成功
    • 202 Accepted : 表示一个请求已经进入后台排队(异步任务)
    • 204 No Content : 请求执行成功,不返回相应资源数据,如 PATCH , DELETE 成功
  • 300 段资源重定向,重定向的新地址都需要在响应头 Location 中返回

    • 301 Moved Permanently : 被请求的资源已永久移动到新位置,需 location
    • 302 Found : 请求的资源临时移动到新位置,需 location
    • 304 Not Modified : 资源自从上次请求后没有再次发生变化,主要使用场景在于实现数据缓存
  • 400 段表示客户端错误

    • 400 Bad Request : 请求体包含语法错误(请求格式错误)
    • 401 Unauthorized : 用户没有权限(令牌、用户名、密码错误)
    • 403 Forbidden : 表示用户验证成功,但是没有该资源权限。
    • 404 Not Found : 找不到目标资源
    • 405 Method Not Allowed : 不允许执行目标方法,响应中应该带有 Allow 头,内容为对该资源有效的 HTTP 方法
    • 406 Not Acceptable : 服务器不支持客户端请求的内容格式,但响应里会包含服务端能够给出的格式的数据,并在 Content-Type 中声明格式名称
    • 409 Conflict : 请求操作和资源的当前状态存在冲突。主要使用场景在于实现并发控制
    • 410 Gone : 被请求的资源已被删除,只有在确定了这种情况是永久性的时候才可以使用,否则建议使用 404 Not Found
    • 422 Unprocessable Entity : 请求格式正确,但是由于含有语义错误(校验错误),无法响应
  • 500 段表示服务器错误

    • 500 Internal Server Error : 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。
    • 501 Not Implemented : 服务器不支持当前请求所需要的某个功能。
    • 502 Bad Gateway : 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。
    • 503 Service Unavailable : 由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。如果能够预计延迟时间,那么响应中可以包含一个 Retry-After 头用以标明这个延迟时间(内容可以为数字,单位为秒;或者是一个 HTTP 协议指定的时间格式)。如果没有给出这个 Retry-After 信息,那么客户端应当以处理 500 响应的方式处理它。