RESTFul API 设计规范

前端之家收集整理的这篇文章主要介绍了RESTFul API 设计规范前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

0. API设计满足关键点

  1. API应当基于 web 标准来设计
  2. API应当对开发者友好并且便于在浏览器地址栏中浏览和探索
  3. API应当是简单、直观和一致的,使它用起来方便和舒适
  4. API应当是高效的,同时要维持和其他需求之间的平衡

1. API命名

1.1 根地址

好的RESTful API要基于HTTPS来发布 API规模不大时,在域名后面增加 api 目录,如:https://www.trawe.cn/api/ API规模很大时,使用以api开头的二级域名,如:https://api.trawe.cn/

1.2 版本问题

  1. 新版本尽量对旧版本作兼容

  2. 版本信息放在URL中

  1. https://api.trawe.cn/v1.2/users/123@H_301_35@
    1. 协议报文中增加version字段
  2. {
  3.     version: "1.0",.... ....
  4. }@H_301_35@ 
    1. HTTP Header增加版本信息
  5.  
  6. 使用已的HTTPHeaderAccept HeaderAccept: application/json+v1.2
  7. 自定义 Header             X-Api-Version: 1.2@H_301_35@ 
  8. 1.3 端点设计原则

  9. 1) 命名

    1. CRUD操作一律使用名词,不使用动词
    1. url一律使用小写字母
    1. url命名方式不使用 camel方式,采用 - 连接两个单词,如:app-setups,而不是appSetups
    1. 请求参数命名方式使用 下划线 _ 连接两个单词(Javascript规范),如:user_name,而不是userNameuser-name
    1. url中不要出现 get / add / delete / put / modify / update 等动词,使用HTTP Method来替代
  10.  
  11. 2) 无论对于单个资源还是集合,名词都使用复数形式,这样便于风格的统一

  12. GET  /tickets      # 获取 tickets 列表
  13. GET  /tickets/12     # 获取一个单独的 ticket
  14. POST /tickets        # 创建一个新的 ticket
  15. PUT  /tickets/12         # 更新 ticket #12
  16. PATCH /tickets/12     # 部分更新 ticket #12
  17. DELETE /tickets/12   #  删除 ticket #12
  18. GET /tickets/12/messages     #  获取ticket #12下的消息列表
  19. GET /tickets/12/messages/5     #  获取ticket #12下的编号为5的消息
  20. POST /tickets/12/messages     #  为ticket #12创建一个新消息
  21. PUT /tickets/12/messages/5     #  更新ticket #12下的编号为5的消息
  22. PATCH /tickets/12/messages/5     #  部分更新ticket #12下的编号为5的消息
  23. DELETE /tickets/12/messages/5    #  删除ticket #12下的编号为5的消息@H_301_35@ 
  24. 3) 对于非CRUD的操作 有很非CRUD服务,可以把这些服务看成资源,计算的结果是资源的presentation,按服务属性选择合适的HTTP方法

  25. 1. 重新构造这个Action,使得它像一个资源的操作。
  26.     这种方法Action不包含参数的情况下可以奏效。例如一个有效的action可以映射成布尔类型field,并且可以通过PATCH更新资源。
  27. 2. 利用RESTful原则像处理子资源一样处理它。
  28.     例如:GithubAPI让你通过PUT /gists/:id/star  star a gist ,而通过DELETE /gists/:id/star来进行 unstar 
  29. 3. 有时候你实在是没有办法将Action映射到任何有意义的RESTful结构。
  30.     例如:多资源搜索没办法真正地映射到任何一个资源接入点。这种情况,/search 将非常有意义,虽然它不是一个名词,但是这样做没有问题,只需要从API消费者的角度做正确的事,并确保所做的一切都用文档清晰记录下来了即可。@H_301_35@ 
  31. 4) 查询过滤 过滤 对每一个字段使用一个唯一查询参数,就可以实现过滤。 例如: 当通过 /tickets 终端来请求一个票据列表时,我们需要增加一些限定来查询那些在售的票。可以使用 GET /tickets?state=open 这样的请求来实现。这里“state”是一个实现了过滤功能查询参数。

  32. 对于常用的查询,有以下两种处理:

    1. 可以单独将查询包装为一个独立的API 如:GET /trades?status=closed&sort=created,desc 可以包装GET /trades/recently-closed
    1. 查询结果标签 将经常使用的、复杂的查询标签化,降低维护成本。如:GET /trades?status=closed&sort=created,desc 可以标签化为 GET /trades#recently-closed
  33.  
  34. 排序 跟过滤类似,使用排序参数字段来描述排序的规则。 为适应复杂排序需求,让排序参数采取逗号分隔的字段列表的形式,每一个字段前都可能有一个负号来表示按降序排序。 例如:

  35. 排序字段前面的 +表示升序   -表示降序  默认为升序
  36. GET /tickets?sort=-priority  # 获取票据列表,按优先级字段降序排序
  37. GET /tickets?sort=-priority,created_at  # 获取票据列表,按“priority”字段降序排序。在一个特定的优先级内,较早的票排在前面。@H_301_35@ 
  38. 5) 减少层级深度 /url中表达层级,用于按实体关联关系进行对象导航,一般根据id导航。 过深的导航容易导致url膨胀,不易维护,如 GET /zoos/1/areas/3/animals/4,尽量使用查询参数代替路径中的实体导航,如GET /animals?zoo=1&area=3

  39. 6) 限制返回哪些字段 使用一个字段查询参数,它包含一个 用逗号隔开的字段列表。例如,下列请求获得的信息将刚刚足够展示一个在售票的有序列表: GET /tickets?fields=id,subject,customer_name,updated_at&state=open&sort=-updated_at

  40. 7) 关于返回结果 通常情况下只返回JSON格式结果。 返回结果支持gzip压缩:进行gzip压缩的数据可节省50%以上的带宽 对于需要返回不同格式资源的情况:

    1. HTTP Header中指定格式 如:Accept:application/xml;q=0.6,application/atom+xml;q=1.0
    1. URL后缀增加扩展名: 如:/users/1.xml
  41.  
  42. 8) 分页 常用的分页字段如下:

  43. limit=10:指定返回记录的数量
  44. offset=10:指定返回记录的开始位置。
  45. page=2&per_page=100:指定第几页,以及每页的记录数。@H_301_35@ 
  46. 9) 缓存 ETag 当产生一个请求时,包含一个HTTP 头,ETag会在里面置入一个和表达内容对应的哈希值或校验值。这个值应当跟随表达内容的变化而变化。现在,如果一个入站HTTP请求包含了一个If-None-Match头和一个匹配的ETag值,API应当返回一个304修改状态码,而不是返回请求的资源。 Last-Modified 基本上像ETag那样工作,不同的是它使用时间戳。在响应头中,Last-Modified包含了一个RFC 1123格式的时间戳,它使用If-Modified-Since来进行验证。注意,HTTP规范已经有了 3 种不同的可接受的日期格式 ,服务器应当准备好接收其中的任何一种。

  47. 2. HTTP方法

  48. 常用:

  49. GET (选择):从服务器上获取一个具体的资源或者一个资源列表。
  50. POST (创建): 在服务器上创建一个新的资源。
  51. PUT (更新):以整体的方式更新服务器上的一个资源。
  52. PATCH (更新):只更新服务器上一个资源的一个属性
  53. DELETE 删除):删除服务器上的一个资源。@H_301_35@ 
  54. 不常用:

  55. HEAD  获取一个资源的元数据,如数据的哈希值或最后的更新时间。
  56. OPTIONS获取客户端能对资源做什么操作的信息。@H_301_35@ 
  57. 3. 数据报文

    1. 统一报文格式
  58.  
  59. 请求使用JSON格式时:
  60. {
  61.     method: 'testMethod',// 请求方法名称
  62.     version: '1.0',// 接口版本
  63.     token: '0X十六进制',//  Token
  64.     sign_type: 'MD5',//  签名算法
  65.     sign: '0X十六进制',//  签名
  66.     timestamp: '12345678'    // 请求的时间戳
  67.     
  68. }
  69. 响应:
  70. {
  71.     code: 0,// 返回码。 -1:失败  0:成功  其它:具体业务代码
  72.     message: '处理成功',// 返回码描述信息
  73.     sign: '0X十六进制',// 签名
  74.     ...                                   // 业务数据
  75. }@H_301_35@ 
  76. 响应数据不作多余包装,如下为错误示例:

  77. {
  78.     code: 0,data: {userName: "用户名"} // 这里面的data没有业务含意,仅仅是为了包装,所以应该去掉
  79. }@H_301_35@ 
  80. 需要进行包装的情况:

    1. 使用JSONP进行跨域请求
    1. 当客户端没有能力处理HTTP头信息时
  81.  
    1. 对于日期类型的字段处理
  82.  
    1. 一种方式为:转为一定格式的字符串,如 yyyy-MM-dd HH:mm:ss.SSS
    1. 另一种方式为:转为长整型数字时间戳
  83.  
  84. 4. 安全

    1. 使用Https传输数据
    1. 使用Token(如JWT)来标识用户状态并设置失效时间
    1. Token失效后客户端自动重新登录获取新的Token
    1. 发送请求时对参数按ASCII排序计算签名(Hash算法或对称加密算法,可以所有接口统一密钥,也可以一个接口一个密钥),接收到请求后先验证签名
    1. 每个端(AndroidiOS、微信服务号、Web网站)生成一个AppKey
    1. 不设置密码,登录时使用手机+验证码方式登录
  85.  
  86. 5. 文档

    1. 文档须提供从请求到响应整个循环的示例;
    1. 请求应该是可粘贴的例子,要么是可以贴到浏览器的链接,要么是可以贴到终端里的curl示例
    1. 一旦发布一个公开的API,必须承诺 在没有通知的前提下,不会更改API功能
    1. 对于外部可见API的更新,文档必须包含任何将废弃的API的时间表和详情;
  87.  
  88. 6. HTTP状态代码

  89. HTTP定义了一套可以从API返回的有意义的状态代码 这些代码能够用来帮助API使用者对不同的响应做出相应处理。

  90. 200 OK (成功)  -  对一次成功的GET,PUT,PATCH  DELETE的响应。也能够用于一次未产生创建活动的POST
  91. 201 Created (已创建)  -  对一次导致创建活动的POST的响应。 同时结合使用一个位置头信息指向新资源的位置
  92. 204 No Content (没有内容) - 对一次没有返回主体信息(像一次DELETE请求)的请求的响应
  93. 304 Not Modified (未修改) - 当使用HTTP缓存头信息时使用304
  94. 400 Bad Request (错误的请求) - 请求是畸形的,比如无法解析请求体
  95. 401 Unauthorized (未授权) - 当没有提供或提供了无效认证细节时。如果从浏览器使用API,也可以用来触发弹出一次认证请求
  96. 403 Forbidden (禁止访问) - 当认证成功但是认证用户无权访问该资源时
  97. 404 Not Found (未找到) - 当一个不存在的资源被请求时
  98. 405 Method Not Allowed (方法禁止) - 当一个对认证用户禁止HTTP方法被请求时
  99. 410 Gone (已删除) - 表示资源在终端不再可用。当访问老版本API时,作为一个通用响应很有用
  100. 415 Unsupported Media Type (不支持的媒体类型) - 如果请求中包含了不正确的内容类型
  101. 422 Unprocessable Entity (无法处理的实体) - 出现验证错误时使用
  102. 429 Too Many Requests (请求过多) - 当请求由于访问速率限制而被拒绝时@H_301_35@ 
  103. 7. 错误处理

  104. 原则

    1. 不要发生了错误但给2xx响应,客户端可能会缓存成功的http请求;
    1. 正确设置http状态码,不要自定义
    1. Response body 提供 1) 错误代码(日志/问题追查);2) 错误的描述文本(展示给用户)。
  105.  
  106. API 可能抛出两类异常:业务异常和非业务异常。 业务异常由自己的业务代码抛出,表示一个用例的前置条件不满足、业务规则冲突等,比如参数校验不通过、权限校验失败。 非业务类异常表示不在预期内的问题,通常由类库、框架抛出,或由于自己的代码逻辑错误导致,比如数据库连接失败、空指针异常、除0错误等等。

  107. 业务类异常必须提供2种信息:

    1. 如果抛出该类异常,HTTP 响应状态码应该设成什么;
    1. 异常的文本描述;
  108.  
  109. Controller层使用统一的异常拦截器:

    1. 设置 HTTP 响应状态码:对业务类异常,用它指定的 HTTP code;对非业务类异常,统一500
    1. Response Body 错误码:异常类名
    1. Response Body 错误描述:
    1. 对业务类异常,用它指定的错误文本;
    1. 对非业务类异常,线上可以统一文案如“服务器端错误,请稍后再试”;
    1. 开发或测试环境中用异常的 stacktrace,服务器端提供该行为的开关。
  110.  
  111. 8. 超媒体API

  112. RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。 比如,当用户api.example.com的根目录发出请求,会得到这样一个文档。

  113. {"link": {
  114.   "rel":   "collection https://www.example.com/zoos","href":  "https://api.example.com/zoos","title": "List of zoos","type":  "application/vnd.yourformat+json"
  115. }}@H_301_35@ 
  116. 上面代码表示,文档中有一个link属性用户读取这个属性就知道下一步该调用什么API了。rel表示这个API与当前网址的关系(collection关系,并给出该collection的网址),href表示API的路径,title表示API标题type表示返回类型。 Hypermedia API的设计被称为HATEOASGithubAPI就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。

  117. {
  118.   "current_user_url": "https://api.github.com/user","authorizations_url": "https://api.github.com/authorizations",// ...
  119. }@H_301_35@ 
  120. 从上面可以看到,如果想获取当前用户的信息,应该去访问api.github.com/user,然后就得到了下面结果。

  121. {
  122.   "message": "Requires authentication","documentation_url": "https://developer.github.com/v3"
  123. }@H_301_35@ 
  124. 上面代码表示,服务器给出了提示信息,以及文档的网址。

猜你在找的设计模式相关文章