└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # RESTful API 规范 v1.0 2 | 3 | @(rest)[规范] 4 | 5 | [toc] 6 | 7 | ## URI 8 | 9 | ### URI规范 10 | * 不要用大写 11 | * 单词间使用下划线'_' 12 | * 不使用动词,资源要使用名词复数形式,如:user、rooms、tickets 13 | * 层级 `>=` 三层,则使用'?'带参数 14 | > ~~users/1/address/2/citys~~ (bad) 15 | /citys?users=1&address=2; (good) 16 | > 17 | 18 | 19 | ----- 20 | 21 | ##Request 22 | 23 | ### Method 24 | * GET:查询资源 25 | * POST:创建资源 26 | * PUT/PATCH 27 | + PUT:全量更新资源(提供改变后的完整资源) 28 | + PATCH:局部更新资源(仅提供改变的属性) 29 | 30 | * DELETE:删除资源 31 | 32 | --- 33 | 34 | ###安全性与幂等性 35 | * 安全性:任意多次对同一资源操作,都不会导致资源的状态变化 36 | * 幂等性:任意次对同一资源操作,对资源的改变是一样的 37 | |Method|安全性|幂等性| 38 | |------|:---:|:---:| 39 | |GET|√|√| 40 | |POST|×|×| 41 | |PUT|×|√| 42 | |PATCH|×|√| 43 | |DELETE|×|√| 44 | 45 | 46 | --- 47 | 48 | ### 兼容 49 | 很多客户只支持GET/POST请求,一般有两种方式模拟PUT等请求 50 | * 添加_method参数 51 | ```javascript 52 | /users/1?_method=put&name=111 53 | ``` 54 | * 添加X-HTTP-Method-Override请求头 (我们使用这种方式) 55 | ```javascript 56 | X-HTTP-Method-Override: PUT 57 | ``` 58 | 59 | --- 60 | 61 | ### 参数 62 | 63 | ####Method 64 | 65 | ##### GET 66 | * 非id的参数使用'?'方式传输 67 | ```javascript 68 | /users/1?state=closed 69 | ``` 70 | #####POST、PATCH、PUT、DELETE 71 | * 非id的参数使用body传输,并且应该encode 72 | 73 | ####过滤 74 | ?type=1&state=closed 75 | 76 | #### 排序 77 | * `+`升序,如?sort=+create_time,根据create_time升序 78 | * `-`降序,如?sort=-create_time,根据create_time降序 79 | 80 | ####分页 81 | ?limit=10&offset=10 82 | * limit:返回记录数量 83 | * offset:返回记录的开始位置 84 | 85 | ####单参数多字段 86 | 使用`,` 分隔,如 87 | ```javascript 88 | /users/1?fields=name,age,city 89 | ``` 90 | 91 | ####Bookmarker 92 | 93 | 94 | --- 95 | 96 | ## 版本控制 97 | 三种方案: 98 | 1. 在uri中加入版本: /v1/room/1 99 | 2. Accept Header:Accept: v1 100 | 3. 自定义 Header:X-Imweb-Media-Type: imweb.v1 (我们使用此方案) 101 | 102 | 自定义Media-Type参考资料[github](https://developer.github.com/v3/media/#request-specific-version) 103 | 104 | --- 105 | 106 | ## 状态码 107 | ### 成功 108 | 109 | |Code|Method|Describe| 110 | |----|------|--------| 111 | |200|ALL|请求成功并返回实体资源| 112 | |201|POST|创建资源成功| 113 | 114 | 115 | ###客户端错误 116 | |Code|Method|Describe| 117 | |----|------|--------| 118 | |400|ALL|一般是参数错误| 119 | |401|ALL|一般用户验证失败(用户名、密码错误等)| 120 | |403|ALL|一般用户权限校验失败| 121 | |404|ALL|资源不存在(github在权限校验失败的情况下也会返回404,为了防止一些私有接口泄露出去)| 122 | |422|ALL|一般是必要字段缺失或参数格式化问题| 123 | 124 | ###服务器错误 125 | |CODE|METHOD|DESCRIBE| 126 | |----|------|--------| 127 | |500|ALL|服务器未知错误| 128 | 129 | 以上是常见的状态码,完整的状态码列表在这[状态码](http://www.restapitutorial.com/httpstatuscodes.html) 130 | 131 | --- 132 | 133 | 134 | 135 | ##HATEOAS 136 | 在介绍HATEOAS之前,先介绍一下REST的成熟度模型 137 | > 在介绍 HATEOAS 之前,先介绍一下 Richardson 提出的 REST 成熟度模型。该模型把 REST 服务按照成熟度划分成 4 个层次: 138 | * 第一个层次(Level 0)的 Web 服务只是使用 HTTP 作为传输方式,实际上只是远程方法调用(RPC)的一种具体形式。 139 | * 第二个层次(Level 1)的 Web 服务引入了资源的概念。每个资源有对应的标识符和表达。 140 | * 第三个层次(Level 2)的 Web 服务使用不同的 HTTP 方法来进行不同的操作,并且使用 HTTP 状态码来表示不同的结果。如 HTTP GET 方法来获取资源,HTTP DELETE 方法来删除资源。 141 | * 第四个层次(Level 3)的 Web 服务使用 HATEOAS。在资源的表达中包含了链接信息。客户端可以根据链接来发现可以执行的动作。 142 | 143 | ### 简述 144 | >HATEOAS(Hypermedia as the engine of application state)是 REST 架构风格中最复杂的约束,也是构建成熟 REST 服务的核心。它的重要性在于客户端和服务器之间的解耦。 145 | 146 | ### 例子 147 | 148 | #### 分页 149 | request请求,查询user,每页显示10条,从第10条开始显示(第二页) 150 | ```javascript 151 | /users?limit=10&offset=10 152 | ``` 153 | 154 | response 155 | ```javascript 156 | { 157 | data: { 158 | xxxx 159 | }, 160 | meta: { 161 | _link: [ 162 | {rel: 'self', href: 'xxx/users?limit=10&offset=10'}, 163 | {rel: 'first', href: 'xxx/users?limit=10&offset=0', title: 'first page'}, 164 | {rel: 'last', href: 'xxx/users?limit=10&offset=50', title: 'last page'}, 165 | {rel: 'prev', href: 'xxx/users?limit=10&offset=0', title: 'prev page'}, 166 | {rel: 'next', href: 'xxx/users?limit=10&offset=20', title: 'next page'} 167 | ] 168 | } 169 | } 170 | ``` 171 | `_link`返回了5个资源 172 | * rel: 'self',资源本身 173 | * rel: 'first',第一页资源 174 | * rel: 'last',最后一页资源 175 | * rel: 'prev',上一页资源 176 | * rel: 'next',下一页资源 177 | 178 | --- 179 | 180 | ###权限相关 181 | 如用户查询一个订单 182 | 183 | ####普通用户 184 | 185 | request 186 | ```javascript 187 | /orders/1 188 | ``` 189 | 190 | response 191 | ```javascript 192 | { 193 | data: { 194 | xxx 195 | }, 196 | meta: { 197 | _link: [ 198 | {rel: 'self', href: 'xxx/orders/1'}, 199 | {rel: 'related', href: 'xxx/orders/1/payment', title: 'pay the order'} 200 | ] 201 | } 202 | } 203 | ``` 204 | `_link`返回两个资源 205 | * rel: 'self',资源本身 206 | * rel: 'related',与当前资源相关的资源,`/order/1/payment`用户可以使用此资源进行支付 207 | 208 | ####权限用户 209 | 210 | request 211 | ``` 212 | /orders/1 213 | ``` 214 | 215 | response 216 | ```javascript 217 | { 218 | data: { 219 | xxx 220 | }, 221 | meta: { 222 | _link: [ 223 | {rel: 'self', href: 'xxx/orders/1'}, 224 | {rel: 'edit', href: 'xxx/orders/1', title: 'edit the order'}, 225 | {rel: 'delete', href: 'xxx/orders/1', title: 'delete the order'} 226 | ] 227 | } 228 | } 229 | ``` 230 | 231 | 此用户拥有修改与删除订单的权限,因此返回了3个资源 232 | * rel: 'self',资源本身 233 | * rel: 'edit',此用户可修改该资源 234 | * rel: 'delete',此用户可删除该资源 235 | 236 | 237 | ### 常用rel 238 | |rel|describe| 239 | |---|--------| 240 | |self|资源本身,每个资源表述都一个包含此关系| 241 | |edit|指向一个可以编辑当前资源的链接| 242 | |delete|指向一个可以删除当前资源的链接| 243 | |item|如果当前资源表示的是一个集合,则用来指向该集合中的单个资源| 244 | |collection|如果当前资源包含在某个集合中,则用来指向包含该资源的集合| 245 | |related|指向一个与当前资源相关的资源| 246 | |first、last、prev、next|分别用来指向第一个、最后一个、上一个和下一个资源| 247 | 248 | ###HATEOAS总结 249 | 由以上例子可以看出`_link`就是以Hyperlink表述资源与资源之间的关系,这种方式使客户端与服务端能很好的分离开来,只要接口的定义不变,客户端与服务端就可以独立的开发和演变。 250 | 251 | 252 | ##Future 253 | * 利用框架判断行为? 254 | * 利用框架添加HATEOAS表述? 255 | * api的快速查找、返回数据定义,假数据? 256 | 257 | --------------------------------------------------------------------------------