├── LICENSE └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 godruoyi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## RESTful API 设计规范 2 | 3 | 该仓库整理了目前比较流行的 `RESTful api` 设计规范,为了方便讨论规范带来的问题及争议,现把该文档托管于 `Github`,欢迎大家补充!! 4 | 5 | ## Table of Contents 6 | 7 | * [RESTful API 设计规范](#restful-api-设计规范) 8 | * [关于「能愿动词」的使用](#关于能愿动词的使用) 9 | * [Protocol](#protocol) 10 | * [API Root URL](#api-root-url) 11 | * [Versioning](#versioning) 12 | * [在 URL 中嵌入版本编号](#在-url-中嵌入版本编号) 13 | * [通过媒体类型来指定版本信息](#通过媒体类型来指定版本信息) 14 | * [Endpoints](#endpoints) 15 | * [HTTP 动词](#http-动词) 16 | * [Filtering](#filtering) 17 | * [Authentication](#authentication) 18 | * [Response](#response) 19 | * [200 ok](#200-ok) 20 | * [201 Created](#201-created) 21 | * [202 Accepted](#202-accepted) 22 | * [204 No Content](#204-no-content) 23 | * [3xx 重定向](#3xx-重定向) 24 | * [400 Bad Request](#400-bad-request) 25 | * [401 Unauthorized](#401-unauthorized) 26 | * [403 Forbidden](#403-forbidden) 27 | * [404 Not Found](#404-not-found) 28 | * [405 Method Not Allowd](#405-method-not-allowd) 29 | * [406 Not Acceptable](#406-not-acceptable) 30 | * [408 Request Timeout](#408-request-timeout) 31 | * [409 Gonfilct](#409-gonfilct) 32 | * [410 Gone](#410-gone) 33 | * [413 Request Entity Too Large](#413-request-entity-too-large) 34 | * [414 Request-URI Too Long](#414-request-uri-too-long) 35 | * [415 Unsupported Media Type](#415-unsupported-media-type) 36 | * [429 Too Many Request](#429-too-many-request) 37 | * [500 Internal Server Error](#500-internal-server-error) 38 | * [503 Service Unavailable](#503-service-unavailable) 39 | * [版权声明](#版权声明) 40 | * [建议参考](#建议参考) 41 | * [LICENSE](#license) 42 | 43 | ## 关于「能愿动词」的使用 44 | 45 | 为了避免歧义,文档大量使用了「能愿动词」,对应的解释如下: 46 | 47 | * `必须 (MUST)`:绝对,严格遵循,请照做,无条件遵守; 48 | * `一定不可 (MUST NOT)`:禁令,严令禁止; 49 | * `应该 (SHOULD)` :强烈建议这样做,但是不强求; 50 | * `不该 (SHOULD NOT)`:强烈不建议这样做,但是不强求; 51 | * `可以 (MAY)` 和 `可选 (OPTIONAL)` :选择性高一点,在这个文档内,此词语使用较少; 52 | 53 | > 参见:[RFC 2119](http://www.ietf.org/rfc/rfc2119.txt) 54 | 55 | ## Protocol 56 | 57 | 客户端在通过 `API` 与后端服务通信的过程中,`应该` 使用 `HTTPS` 协议。 58 | 59 | ## API Root URL 60 | 61 | `API` 的根入口点应尽可能保持足够简单,这里有两个常见的 `URL` 根例子: 62 | 63 | * api.example.com/* 64 | * example.com/api/* 65 | 66 | > 如果你的应用很庞大或者你预计它将会变的很庞大,那 `应该` 将 `API` 放到子域下(`api.example.com`)。这种做法可以保持某些规模化上的灵活性。 67 | 68 | ## Versioning 69 | 70 | 所有的 `API` 必须保持向后兼容,你 `必须` 在引入新版本 `API` 的同时确保旧版本 `API` 仍然可用。所以 `应该` 为其提供版本支持。 71 | 72 | 目前比较常见的两种版本号形式: 73 | 74 | ### 在 URL 中嵌入版本编号 75 | 76 | ```bash 77 | api.example.com/v1/* 78 | ``` 79 | 80 | 这种做法是版本号直观、易于调试;另一种做法是,将版本号放在 `HTTP Header` 头中: 81 | 82 | ### 通过媒体类型来指定版本信息 83 | 84 | ```bash 85 | Accept: application/vnd.example.com.v1+json 86 | ``` 87 | 88 | 其中 `vnd` 表示 `Standards Tree` 标准树类型,有三个不同的树: `x`,`prs` 和 `vnd`。你使用的标准树需要取决于你开发的项目 89 | 90 | * 未注册的树(`x`)主要表示本地和私有环境 91 | * 私有树(`prs`)主要表示没有商业发布的项目 92 | * 供应商树(`vnd`)主要表示公开发布的项目 93 | 94 | > 后面几个参数依次为应用名称(一般为应用域名)、版本号、期望的返回格式。 95 | 96 | 至于具体把版本号放在什么地方,这个问题一直存在很大的争议,但由于我们大多数时间都在使用 `Laravel` 开发,`应该` 使用 [dingo/api](https://github.com/dingo/api) 来快速构建应用,它采用第二种方式来管理 `API` 版本,并且已集成了标准的 `HTTP Response`。 97 | 98 | ## Endpoints 99 | 100 | 端点就是指向特定资源或资源集合的 `URL`。在端点的设计中,你 `必须` 遵守下列约定: 101 | 102 | * URL 的命名 `必须` 全部小写 103 | * URL 中资源(`resource`)的命名 `必须` 是名词,并且 `必须` 是复数形式 104 | * `必须` 优先使用 `Restful` 类型的 URL 105 | * URL `必须` 是易读的 106 | * URL `一定不可` 暴露服务器架构 107 | 108 | > 至于 URL 是否必须使用连字符(`-`) 或下划线(`_`),不做硬性规定,但 `必须` 根据团队情况统一一种风格。 109 | 110 | 来看一个反例 111 | 112 | * https://api.example.com/getUserInfo?userid=1 113 | * https://api.example.com/getusers 114 | * https://api.example.com/sv/u 115 | * https://api.example.com/cgi-bin/users/get_user.php?userid=1 116 | 117 | 再来看一个正列 118 | 119 | * https://api.example.com/zoos 120 | * https://api.example.com/animals 121 | * https://api.example.com/zoos/{zoo}/animals 122 | * https://api.example.com/animal_types 123 | * https://api.example.com/employees 124 | 125 | ## HTTP 动词 126 | 127 | 对于资源的具体操作类型,由 `HTTP` 动词表示。常用的 `HTTP` 动词有下面五个(括号里是对应的 `SQL` 命令)。 128 | 129 | * GET(SELECT):从服务器取出资源(一项或多项)。 130 | * POST(CREATE):在服务器新建一个资源。 131 | * PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。 132 | * PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。 133 | * DELETE(DELETE):从服务器删除资源。 134 | 135 | 其中 136 | 137 | 1 删除资源 `必须` 用 `DELETE` 方法 138 | 2 创建新的资源 `必须` 使用 `POST` 方法 139 | 3 更新资源 `应该` 使用 `PUT` 方法 140 | 4 获取资源信息 `必须` 使用 `GET` 方法 141 | 142 | 针对每一个端点来说,下面列出所有可行的 `HTTP` 动词和端点的组合 143 | 144 | | 请求方法 | URL | 描述 | 145 | | ---------- | --- | --- | 146 | | GET | /zoos | 列出所有的动物园(ID和名称,不要太详细) | 147 | | POST | /zoos | 新增一个新的动物园 | 148 | | GET | /zoos/{zoo} | 获取指定动物园详情 | 149 | | PUT | /zoos/{zoo} | 更新指定动物园(整个对象) | 150 | | PATCH | /zoos/{zoo} | 更新动物园(部分对象) | 151 | | DELETE | /zoos/{zoo} | 删除指定动物园 | 152 | | GET | /zoos/{zoo}/animals | 检索指定动物园下的动物列表(ID和名称,不要太详细) | 153 | | GET | /animals | 列出所有动物(ID和名称)。 | 154 | | POST | /animals | 新增新的动物 | 155 | | GET | /animals/{animal} | 获取指定的动物详情 | 156 | | PUT | /animals/{animal} | 更新指定的动物(整个对象) | 157 | | PATCH | /animals/{animal} | 更新指定的动物(部分对象) | 158 | | GET | /animal_types | 获取所有动物类型(ID和名称,不要太详细) | 159 | | GET | /animal_types/{type} | 获取指定的动物类型详情 | 160 | | GET | /employees | 检索整个雇员列表 | 161 | | GET | /employees/{employee} | 检索指定特定的员工 | 162 | | GET | /zoos/{zoo}/employees | 检索在这个动物园工作的雇员的名单(身份证和姓名) | 163 | | POST | /employees | 新增指定新员工 | 164 | | POST | /zoos/{zoo}/employees | 在特定的动物园雇佣一名员工 | 165 | | DELETE | /zoos/{zoo}/employees/{employee} | 从某个动物园解雇一名员工 | 166 | 167 | > 超出 `Restful` 端点的,`应该` 模仿上表的方式来定义端点。 168 | 169 | ## Filtering 170 | 171 | > 如果记录数量很多,服务器不可能都将它们返回给用户。API `应该` 提供参数,过滤返回结果。下面是一些常见的参数。 172 | 173 | * ?limit=10:指定返回记录的数量 174 | * ?offset=10:指定返回记录的开始位置。 175 | * ?page=2&per_page=100:指定第几页,以及每页的记录数。 176 | * ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。 177 | * ?animal_type_id=1:指定筛选条件 178 | 179 | 所有 `URL` 参数 `必须` 是全小写,`必须` 使用下划线类型的参数形式。 180 | 181 | > 分页参数 `必须` 固定为 `page`、`per_page` 182 | 183 | 经常使用的、复杂的查询 `应该` 标签化,降低维护成本。如 184 | 185 | ```bash 186 | GET /trades?status=closed&sort=sortby=name&order=asc 187 | 188 | # 可为其定制快捷方式 189 | GET /trades/recently_closed 190 | ``` 191 | 192 | ## Authentication 193 | 194 | `应该` 使用 `OAuth2.0` 的方式为 API 调用者提供登录认证。`必须` 先通过登录接口获取 `Access Token` 后再通过该 `token` 调用需要身份认证的 `API`。 195 | 196 | Oauth 的端点设计示列 197 | 198 | * RFC 6749 /token 199 | * Twitter /oauth2/token 200 | * Fackbook /oauth/access_token 201 | * Google /o/oauth2/token 202 | * Github /login/oauth/access_token 203 | * Instagram /oauth/authorize 204 | 205 | 客户端在获得 `access token` 的同时 `必须` 在响应中包含一个名为 `expires_in` 的数据,它表示当前获得的 `token` 会在多少 `秒` 后失效。 206 | 207 | ```json 208 | { 209 | "access_token": "token....", 210 | "token_type": "Bearer", 211 | "expires_in": 3600 212 | } 213 | ``` 214 | 215 | 客户端在请求需要认证的 `API` 时,`必须` 在请求头 `Authorization` 中带上 `access_token`。 216 | 217 | ```bash 218 | Authorization: Bearer token... 219 | ``` 220 | 221 | 当超过指定的秒数后,`access token` 就会过期,再次用过期/或无效的 `token` 访问时,服务端 `应该` 返回 `invalid_token` 的错误或 `401` 错误码。 222 | 223 | ```http 224 | HTTP/1.1 401 Unauthorized 225 | Content-Type: application/json 226 | Cache-Control: no-store 227 | Pragma: no-cache 228 | 229 | { 230 | "error": "invalid_token" 231 | } 232 | ``` 233 | 234 | > Laravel 开发中,`应该` 使用 [JWT](https://github.com/tymondesigns/jwt-auth) 来为管理你的 Token,并且 `一定不可` 在 `api` 中间件中开启请求 `session`。 235 | 236 | ## Response 237 | 238 | 所有的 `API` 响应,`必须` 遵守 `HTTP` 设计规范,`必须` 选择合适的 `HTTP` 状态码。`一定不可` 所有接口都返回状态码为 `200` 的 `HTTP` 响应,如: 239 | 240 | ```http 241 | HTTP/1.1 200 ok 242 | Content-Type: application/json 243 | Server: example.com 244 | 245 | { 246 | "code": 0, 247 | "msg": "success", 248 | "data": { 249 | "username": "username" 250 | } 251 | } 252 | ``` 253 | 254 | 或 255 | 256 | ```http 257 | HTTP/1.1 200 ok 258 | Content-Type: application/json 259 | Server: example.com 260 | 261 | { 262 | "code": -1, 263 | "msg": "该活动不存在", 264 | } 265 | ``` 266 | 267 | 下表列举了常见的 `HTTP` 状态码 268 | 269 | | 状态码 | 描述 | 270 | | ---------- | --- | 271 | | 1xx | 代表请求已被接受,需要继续处理 | 272 | | 2xx | 请求已成功,请求所希望的响应头或数据体将随此响应返回 | 273 | | 3xx | 重定向 | 274 | | 4xx | 客户端原因引起的错误 | 275 | | 5xx | 服务端原因引起的错误 | 276 | 277 | > 只有来自客户端的请求被正确的处理后才能返回 `2xx` 的响应,所以当 API 返回 `2xx` 类型的状态码时,前端 `必须` 认定该请求已处理成功。 278 | 279 | 必须强调的是,所有 `API` `一定不可` 返回 `1xx` 类型的状态码。当 `API` 发生错误时,`必须` 返回出错时的详细信息。目前常见返回错误信息的方法有两种: 280 | 281 | 1、将错误详细放入 `HTTP` 响应首部; 282 | 283 | ```http 284 | X-MYNAME-ERROR-CODE: 4001 285 | X-MYNAME-ERROR-MESSAGE: Bad authentication token 286 | X-MYNAME-ERROR-INFO: http://docs.example.com/api/v1/authentication 287 | ``` 288 | 289 | 2、直接放入响应实体中; 290 | 291 | ```http 292 | HTTP/1.1 401 Unauthorized 293 | Server: nginx/1.11.9 294 | Content-Type: application/json 295 | Transfer-Encoding: chunked 296 | Cache-Control: no-cache, private 297 | Date: Sun, 24 Jun 2018 10:02:59 GMT 298 | Connection: keep-alive 299 | 300 | {"error_code":40100,"message":"Unauthorized"} 301 | ``` 302 | 303 | 考虑到易读性和客户端的易处理性,我们 `必须` 把错误信息直接放到响应实体中,并且错误格式 `应该` 满足如下格式: 304 | 305 | ```json 306 | { 307 | "message": "您查找的资源不存在", 308 | "error_code": 404001 309 | } 310 | ``` 311 | 312 | 其中错误码(`error_code`)`必须` 和 `HTTP` 状态码对应,也方便错误码归类,如: 313 | 314 | ```http 315 | HTTP/1.1 429 Too Many Requests 316 | Server: nginx/1.11.9 317 | Content-Type: application/json 318 | Transfer-Encoding: chunked 319 | Cache-Control: no-cache, private 320 | Date: Sun, 24 Jun 2018 10:15:52 GMT 321 | Connection: keep-alive 322 | 323 | {"error_code":429001,"message":"你操作太频繁了"} 324 | ``` 325 | 326 | ```http 327 | HTTP/1.1 403 Forbidden 328 | Server: nginx/1.11.9 329 | Content-Type: application/json 330 | Transfer-Encoding: chunked 331 | Cache-Control: no-cache, private 332 | Date: Sun, 24 Jun 2018 10:19:27 GMT 333 | Connection: keep-alive 334 | 335 | {"error_code":403002,"message":"用户已禁用"} 336 | ``` 337 | 338 | `应该` 在返回的错误信息中,同时包含面向开发者和面向用户的提示信息,前者可方便开发人员调试,后者可直接展示给终端用户查看如: 339 | 340 | ```json 341 | { 342 | "message": "直接展示给终端用户的错误信息", 343 | "error_code": "业务错误码", 344 | "error": "供开发者查看的错误信息", 345 | "debug": [ 346 | "错误堆栈,必须开启 debug 才存在" 347 | ] 348 | } 349 | ``` 350 | 351 | 下面详细列举了各种情况 API 的返回说明。 352 | 353 | ### 200 ok 354 | 355 | `200` 状态码是最常见的 `HTTP` 状态码,在所有 **成功** 的 `GET` 请求中,`必须` 返回此状态码。`HTTP` 响应实体部分 `必须` 直接就是数据,不要做多余的包装。 356 | 357 | 错误示例: 358 | 359 | ```http 360 | HTTP/1.1 200 ok 361 | Content-Type: application/json 362 | Server: example.com 363 | 364 | { 365 | "user": { 366 | "id":1, 367 | "nickname":"fwest", 368 | "username": "example" 369 | } 370 | } 371 | ``` 372 | 373 | 正确示例: 374 | 375 | 1、获取单个资源详情 376 | 377 | ```json 378 | { 379 | "id": 1, 380 | "username": "godruoyi", 381 | "age": 88, 382 | } 383 | ``` 384 | 385 | 2、获取资源集合 386 | 387 | ```json 388 | [ 389 | { 390 | "id": 1, 391 | "username": "godruoyi", 392 | "age": 88, 393 | }, 394 | { 395 | "id": 2, 396 | "username": "foo", 397 | "age": 88, 398 | } 399 | ] 400 | ``` 401 | 402 | 3、额外的媒体信息 403 | 404 | ```json 405 | { 406 | "data": [ 407 | { 408 | "id": 1, 409 | "avatar": "https://lorempixel.com/640/480/?32556", 410 | "nickname": "fwest", 411 | "last_logined_time": "2018-05-29 04:56:43", 412 | "has_registed": true 413 | }, 414 | { 415 | "id": 2, 416 | "avatar": "https://lorempixel.com/640/480/?86144", 417 | "nickname": "zschowalter", 418 | "last_logined_time": "2018-06-16 15:18:34", 419 | "has_registed": true 420 | } 421 | ], 422 | "meta": { 423 | "pagination": { 424 | "total": 101, 425 | "count": 2, 426 | "per_page": 2, 427 | "current_page": 1, 428 | "total_pages": 51, 429 | "links": { 430 | "next": "http://api.example.com?page=2" 431 | } 432 | } 433 | } 434 | } 435 | ``` 436 | 437 | > 其中,分页和其他额外的媒体信息,必须放到 `meta` 字段中。 438 | 439 | ### 201 Created 440 | 441 | 当服务器创建数据成功时,`应该` 返回此状态码。常见的应用场景是使用 `POST` 提交用户信息,如: 442 | 443 | * 添加了新用户 444 | * 上传了图片 445 | * 创建了新活动 446 | 447 | 等,都可以返回 `201` 状态码。需要注意的是,你可以选择在用户创建成功后返回新用户的数据 448 | 449 | ```http 450 | HTTP/1.1 201 Created 451 | Server: nginx/1.11.9 452 | Content-Type: application/json 453 | Transfer-Encoding: chunked 454 | Date: Sun, 24 Jun 2018 09:13:40 GMT 455 | Connection: keep-alive 456 | 457 | { 458 | "id": 1, 459 | "avatar": "https:\/\/lorempixel.com\/640\/480\/?32556", 460 | "nickname": "fwest", 461 | "last_logined_time": "2018-05-29 04:56:43", 462 | "created_at": "2018-06-16 17:55:55", 463 | "updated_at": "2018-06-16 17:55:55" 464 | } 465 | ``` 466 | 467 | 也可以返回一个响应实体为空的 `HTTP Response` 如: 468 | 469 | ```http 470 | HTTP/1.1 201 Created 471 | Server: nginx/1.11.9 472 | Content-Type: text/html; charset=UTF-8 473 | Transfer-Encoding: chunked 474 | Date: Sun, 24 Jun 2018 09:12:20 GMT 475 | Connection: keep-alive 476 | ``` 477 | 478 | > 这里我们 `应该` 采用第二种方式,因为大多数情况下,客户端只需要知道该请求操作成功与否,并不需要返回新资源的信息。 479 | 480 | ### 202 Accepted 481 | 482 | 该状态码表示服务器已经接受到了来自客户端的请求,但还未开始处理。常用短信发送、邮件通知、模板消息推送等这类很耗时需要队列支持的场景中; 483 | 484 | > 返回该状态码时,响应实体 `必须` 为空。 485 | 486 | ```html 487 | HTTP/1.1 202 Accepted 488 | Server: nginx/1.11.9 489 | Content-Type: text/html; charset=UTF-8 490 | Transfer-Encoding: chunked 491 | Date: Sun, 24 Jun 2018 09:25:15 GMT 492 | Connection: keep-alive 493 | ``` 494 | 495 | ### 204 No Content 496 | 497 | 该状态码表示响应实体不包含任何数据,其中: 498 | 499 | * 在使用 `DELETE` 方法删除资源 **成功** 时,`必须` 返回该状态码 500 | * 使用 `PUT`、`PATCH` 方法更新数据 **成功** 时,也 `应该` 返回此状态码 501 | 502 | ```http 503 | HTTP/1.1 204 No Content 504 | Server: nginx/1.11.9 505 | Date: Sun, 24 Jun 2018 09:29:12 GMT 506 | Connection: keep-alive 507 | ``` 508 | 509 | ### 3xx 重定向 510 | 511 | 所有 `API` `不该` 返回 `3xx` 类型的状态码。因为 `3xx` 类型的响应格式一般为下列格式: 512 | 513 | ```html 514 | HTTP/1.1 302 Found 515 | Server: nginx/1.11.9 516 | Content-Type: text/html; charset=UTF-8 517 | Transfer-Encoding: chunked 518 | Cache-Control: no-cache, private 519 | Date: Sun, 24 Jun 2018 09:41:50 GMT 520 | Location: https://example.com 521 | Connection: keep-alive 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | Redirecting to https://example.com 530 | 531 | 532 | Redirecting to https://example.com. 533 | 534 | 535 | ``` 536 | 537 | 所有 `API` `一定不可` 返回纯 `HTML` 结构的响应;若一定要使用重定向功能,`可以` 返回一个响应实体为空的 `3xx` 响应,并在响应头中加上 `Location` 字段: 538 | 539 | ```http 540 | HTTP/1.1 302 Found 541 | Server: nginx/1.11.9 542 | Content-Type: text/html; charset=UTF-8 543 | Transfer-Encoding: chunked 544 | Date: Sun, 24 Jun 2018 09:52:50 GMT 545 | Location: https://godruoyi.com 546 | Connection: keep-alive 547 | ``` 548 | 549 | ### 400 Bad Request 550 | 551 | 由于明显的客户端错误(例如,请求语法格式错误、无效的请求、无效的签名等),服务器 `应该` 放弃该请求。 552 | 553 | > 当服务器无法从其他 4xx 类型的状态码中找出合适的来表示错误类型时,都 `必须` 返回该状态码。 554 | 555 | ```http 556 | HTTP/1.1 400 Bad Request 557 | Server: nginx/1.11.9 558 | Content-Type: application/json 559 | Transfer-Encoding: chunked 560 | Cache-Control: no-cache, private 561 | Date: Sun, 24 Jun 2018 13:22:36 GMT 562 | Connection: keep-alive 563 | 564 | {"error_code":40000,"message":"无效的签名"} 565 | ``` 566 | 567 | ### 401 Unauthorized 568 | 569 | 该状态码表示当前请求需要身份认证,以下情况都 `必须` 返回该状态码。 570 | 571 | * 未认证用户访问需要认证的 API 572 | * access_token 无效/过期 573 | 574 | > 客户端在收到 `401` 响应后,都 `应该` 提示用户进行下一步的登录操作。 575 | 576 | ```http 577 | HTTP/1.1 401 Unauthorized 578 | Server: nginx/1.11.9 579 | Content-Type: application/json 580 | Transfer-Encoding: chunked 581 | WWW-Authenticate: JWTAuth 582 | Cache-Control: no-cache, private 583 | Date: Sun, 24 Jun 2018 13:17:02 GMT 584 | Connection: keep-alive 585 | 586 | {"message":"Token Signature could not be verified.","error_code": "40100"} 587 | ``` 588 | 589 | ### 403 Forbidden 590 | 591 | 该状态码可以简单的理解为没有权限访问该请求,服务器收到请求但拒绝提供服务。 592 | 593 | 如当普通用户请求操作管理员用户时,`必须` 返回该状态码。 594 | 595 | ```http 596 | HTTP/1.1 403 Forbidden 597 | Server: nginx/1.11.9 598 | Content-Type: application/json 599 | Transfer-Encoding: chunked 600 | Cache-Control: no-cache, private 601 | Date: Sun, 24 Jun 2018 13:05:34 GMT 602 | Connection: keep-alive 603 | 604 | {"error_code":40301,"message":"权限不足"} 605 | ``` 606 | 607 | ### 404 Not Found 608 | 609 | 该状态码表示用户请求的资源不存在,如 610 | 611 | * 获取不存在的用户信息 (get /users/9999999) 612 | * 访问不存在的端点 613 | 614 | 都 `必须` 返回该状态码,若该资源已永久不存在,则 `应该` 返回 `410` 响应。 615 | 616 | ### 405 Method Not Allowed 617 | 618 | 当客户端使用的 `HTTP` 请求方法不被服务器允许时,`必须` 返回该状态码。 619 | 620 | > 如客户端调用了 `POST` 方法来访问只支持 GET 方法的 API 621 | 622 | 该响应 `必须` 返回一个 `Allow` 头信息用以表示出当前资源能够接受的请求方法的列表。 623 | 624 | ```http 625 | HTTP/1.1 405 Method Not Allowed 626 | Server: nginx/1.11.9 627 | Content-Type: application/json 628 | Transfer-Encoding: chunked 629 | Allow: GET, HEAD 630 | Cache-Control: no-cache, private 631 | Date: Sun, 24 Jun 2018 12:30:57 GMT 632 | Connection: keep-alive 633 | 634 | {"message":"405 Method Not Allowed","error_code": 40500} 635 | ``` 636 | 637 | ### 406 Not Acceptable 638 | 639 | `API` 在不支持客户端指定的数据格式时,应该返回此状态码。如支持 `JSON` 和 `XML` 输出的 `API` 被指定返回 `YAML` 格式的数据时。 640 | 641 | > Http 协议一般通过请求首部的 Accept 来指定数据格式 642 | 643 | ### 408 Request Timeout 644 | 645 | 客户端请求超时时 `必须` 返回该状态码,需要注意的时,该状态码表示 **客户端请求超时**,在涉及第三方 `API` 调用超时时,`一定不可` 返回该状态码。 646 | 647 | ### 409 Confilct 648 | 649 | 该状态码表示因为请求存在冲突无法处理。如通过手机号码提供注册功能的 `API`,当用户提交的手机号已存在时,`必须` 返回此状态码。 650 | 651 | ```http 652 | HTTP/1.1 409 Conflict 653 | Server: nginx/1.11.9 654 | Content-Type: application/json 655 | Transfer-Encoding: chunked 656 | Cache-Control: no-cache, private 657 | Date: Sun, 24 Jun 2018 12:19:04 GMT 658 | Connection: keep-alive 659 | 660 | {"error_code":40900,"message":"手机号已存在"} 661 | ``` 662 | 663 | ### 410 Gone 664 | 665 | 和 `404` 类似,该状态码也表示请求的资源不存在,只是 `410` 状态码进一步表示所请求的资源已不存在,并且未来也不会存在。在收到 `410` 状态码后,客户端 `应该` 停止再次请求该资源。 666 | 667 | ### 413 Request Entity Too Large 668 | 669 | 该状态码表示服务器拒绝处理当前请求,因为该请求提交的实体数据大小超过了服务器愿意或者能够处理的范围。 670 | 671 | > 此种情况下,服务器可以关闭连接以免客户端继续发送此请求。 672 | 673 | 如果这个状况是临时的,服务器 `应该` 返回一个 `Retry-After` 的响应头,以告知客户端可以在多少时间以后重新尝试。 674 | 675 | ### 414 Request-URI Too Long 676 | 677 | 该状态码表示请求的 `URI` 长度超过了服务器能够解释的长度,因此服务器拒绝对该请求提供服务。 678 | 679 | ### 415 Unsupported Media Type 680 | 681 | 通常表示服务器不支持客户端请求首部 `Content-Type` 指定的数据格式。如在只接受 `JSON` 格式的 `API` 中放入 `XML` 类型的数据并向服务器发送,都 `应该` 返回该状态码。 682 | 683 | 该状态码也可用于如:只允许上传图片格式的文件,但是客户端提交媒体文件非法或不是图片类型,这时 `应该` 返回该状态码: 684 | 685 | ```http 686 | HTTP/1.1 415 Unsupported Media Type 687 | Server: nginx/1.11.9 688 | Content-Type: application/json 689 | Transfer-Encoding: chunked 690 | Cache-Control: no-cache, private 691 | Date: Sun, 24 Jun 2018 12:09:40 GMT 692 | Connection: keep-alive 693 | 694 | {"error_code":41500,"message":"不允许上传的图片格式"} 695 | ``` 696 | 697 | ### 429 Too Many Requests 698 | 699 | 该状态码表示用户请求次数超过允许范围。如 `API` 设定为 `60次/分钟`,当用户在一分钟内请求次数超过 60 次后,都 `应该` 返回该状态码。并且也 `应该` 在响应首部中加上下列头部: 700 | 701 | ```bash 702 | X-RateLimit-Limit: 10 请求速率(由应用设定,其单位一般为小时/分钟等,这里是 10次/5分钟) 703 | X-RateLimit-Remaining: 0 当前剩余的请求数量 704 | X-RateLimit-Reset: 1529839462 重置时间 705 | Retry-After: 120 下一次访问应该等待的时间(秒) 706 | ``` 707 | 708 | 列子 709 | 710 | ```http 711 | HTTP/1.1 429 Too Many Requests 712 | Server: nginx/1.11.9 713 | Content-Type: application/json 714 | Transfer-Encoding: chunked 715 | X-RateLimit-Limit: 10 716 | X-RateLimit-Remaining: 0 717 | X-RateLimit-Reset: 1529839462 718 | Retry-After: 290 719 | Cache-Control: no-cache, private 720 | Date: Sun, 24 Jun 2018 11:19:32 GMT 721 | Connection: keep-alive 722 | 723 | {"message":"You have exceeded your rate limit.","error_code":42900} 724 | ``` 725 | 726 | `必须` 为所有的 API 设置 Rate Limit 支持。 727 | 728 | ### 500 Internal Server Error 729 | 730 | 该状态码 `必须` 在服务器出错时抛出,对于所有的 `500` 错误,都 `应该` 提供完整的错误信息支持,也方便跟踪调试。 731 | 732 | ### 503 Service Unavailable 733 | 734 | 该状态码表示服务器暂时处理不可用状态,当服务器需要维护或第三方 `API` 请求超时/不可达时,都 `应该` 返回该状态码,其中若是主动关闭 API 服务,`应该 `在返回的响应首部加上 `Retry-After` 头部,表示多少秒后可以再次访问。 735 | 736 | ```http 737 | HTTP/1.1 503 Service Unavailable 738 | Server: nginx/1.11.9 739 | Content-Type: application/json 740 | Transfer-Encoding: chunked 741 | Cache-Control: no-cache, private 742 | Date: Sun, 24 Jun 2018 10:56:20 GMT 743 | Retry-After: 60 744 | Connection: keep-alive 745 | 746 | {"error_code":50300,"message":"服务维护中"} 747 | ``` 748 | 749 | 其他 `HTTP` 状态码请参考 [HTTP 状态码- 维基百科](https://zh.wikipedia.org/zh-hans/HTTP%E7%8A%B6%E6%80%81%E7%A0%81)。 750 | 751 | ## 版权声明 752 | 753 | > 版权声明:自由转载-非商用-非衍生-保持署名([创意共享3.0许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh)) 754 | 755 | ## 建议参考 756 | 757 | [restful-api-design-references](https://github.com/aisuhua/restful-api-design-references) 758 | 759 | [Principles of good RESTful API Design(译)](http://www.cnblogs.com/moonz-wu/p/4211626.html) 760 | 761 | [理解 RESTful 架构](http://www.ruanyifeng.com/blog/2011/09/restful.html) 762 | 763 | [RESTful API 设计指南](http://www.ruanyifeng.com/blog/2014/05/restful_api.html) 764 | 765 | [HTTP 状态码- 维基百科](https://zh.wikipedia.org/zh-hans/HTTP%E7%8A%B6%E6%80%81%E7%A0%81) 766 | 767 | ## LICENSE 768 | 769 | MIT License 770 | 771 | Copyright (c) 2018 godruoyi 772 | 773 | Permission is hereby granted, free of charge, to any person obtaining a copy 774 | of this software and associated documentation files (the "Software"), to deal 775 | in the Software without restriction, including without limitation the rights 776 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 777 | copies of the Software, and to permit persons to whom the Software is 778 | furnished to do so, subject to the following conditions: 779 | 780 | The above copyright notice and this permission notice shall be included in all 781 | copies or substantial portions of the Software. 782 | 783 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 784 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 785 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 786 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 787 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 788 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 789 | SOFTWARE. 790 | --------------------------------------------------------------------------------