普元

RESTful API 设计约定

2019-07-10  本文已影响0人  磐不渝

目的

本文编写目的是为了尽可能的约定好一个公司的产品、组件、项目开发的RESTful API 设计风格,使不同团队间设计的API风格尽量一致,减少项目后期由于规范问题或设计不足导致的接口重构造成的开发、测试返工。最终让接口的最终使用者能够在开发过程中有个良好的体验。

此约定可作为开发人员设计RESTful 接口或项目接口发布评审的参考。

个人观点:用了 JSON-RPC 不等于 是RESTful API,RESTful API通常是基于HTTP/JSON方式实现的 ,两种方式的API设计方式都不错,项目中选适合的就好。简单对比如下:

本文仅是作者个人根据主观喜好和接口设计经验搜罗总结而来的RESTful API设计约定,仅作为接口设计的基本要求,也欢迎与大家讨论。此约定未涉及超文本HATEOAS相关内容,也不包含RPC类面向后端服务方法映射接口的范畴。

API 设计的关键点

API 是后端应用程序的脸面(UI),用户体验非常重要。尤其是当你开发的是一个可复用的组件或产品,如果API设计有些许瑕疵,会直接影响开发者的体验,设计者会被骂的…… 有问题的API一旦开放出去了,哪怕是简单的拼写错误,由于要保持兼容性不能删改,会成为技术欠债长期背下去。

以上关键点不只适用于RESTful API,其他类型API也是一样的。

URL Root 根路径定义

作为对外公开发布的RESTful API,根URL中一般要有域名与版本信息。通常一个WEB站点会同时提供网站服务和API服务,我们需要根据URL能够区分出访问的是服务接口还是网站中的网页、文件等资源。因此RESTful API的根URL中根据不同场景一般会在一级域名中或者是子域名中带api关键字。

常见的两种根URL用法如下:

  1. https//api.example.com/v1/* [example.com同时提供网站和API服务]
  2. https//xxx.example.org/api/v1/* [example.com拥有多个子域名,且子域名会同时提供网站和API服务]

推荐的方案是根URL中采用子域名的方式区分API,即: https://iam.primeton.com/api/v1/*

URL Endpoint 路径终点资源命名

路径终点即粗体部分内容: https:// example.org/api/v1/menus

HTTP Method 说明

设计RESTful API时常用的HTTP Method包括:GET、POST、PUT、PATCH、DELETE 简单说明如下:

路径变量与查询参数如何选?

唯一定位单个资源用路径参数

根据资源标识可以唯一定位一个资源时,建议使用URL路径参数方式传递。对应Springboot 的 @PathVariable。API Path示例如下:

过滤一或多个资源用查询参数

根据资源属性查询过滤一或多个资源时,建议使用URL查询参数方式传递。对应Springboot的 @RequestParam。API Path示例如下:

复杂条件查询参数怎么传?

对于简单查询类接口,可以使用路径参数和查询参数解决,如果是复杂功能型查询接口中需要通过复杂的过滤条件查询时如:> < in between等等,查询参数用起来会非常痛苦,GET Method又不支持提交Request Body参数。因此我建议这种复杂型查询采用POST Method 提交到一个特定的Path上。参见如下场景:

{
  "table" : "student",
  "where" : {
    "condition" : {
      "and" : [ {
        "or" : [ {
          "field" : "name",
          "op" : "like",
          "values" : [ "子涵" ]
        }, {
          "field" : "name",
          "op" : "like",
          "values" : [ "梓涵" ]
        } ]
      }, {
        "field" : "age",
        "op" : "between",
        "values" : [ 18, 20 ]
      }, {
        "field" : "address",
        "op" : "like",
        "values" : [ "上海" ]
      } ]
    }
  },
  "select" : [ "name", "age", "mail", "address" ],
  "order" : [ {
    "field" : "age",
    "type" : "desc"
  } ]
}

分页与排序

查询接口返回多个数据时,需要支持分页(枚举类数据或少量数据除外)和排序。

如需使用分页查询和排序,建议统一请求与响应报文结构,格式如下:

请求参数示例:

GET /users?page=1&size=5&sort=username

单页数据响应结果示例:

{
    "content": [
        {
            "id": 6,
            "username": "allen"
        },
        {
            "id": 7,
            "username": "alice"
        },
        {
            "id": 8,
            "username": "bob"
        },
        {
            "id": 9,
            "username": "brown"
        },
        {
            "id": 10,
            "username": "bond"
        }
    ],
    "totalPages": 4,
    "last": false,
    "totalElements": 19,
    "number": 1,
    "size": 5,
    "sort": "username",
    "first": false,
    "numberOfElements": 5
}

上述分页排序与响应报文格式是来自Spring Data定义的模型,为了保持分页排序接口相关的使用习惯,如果持久化不使用JPA,仍然建议采用上述规范的报文定义封装接口。为使用者提供一致的体验。

资源操作类接设计

如果资源需要做一些增删改之外的操作(如状态变更),可以用/actions作为path

例如:流程平台中的流程实例会有状态变化,如启动、挂起、恢复、终止等等,这种接口建议这样设计:

资源层级关系接口设计

组合资源通过所属资源入口访问

组合资源,即两种资源之间存在组合关系,组合指整体与部分的强包含关系,但整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束。对"部分"的操作一定会由整体作为入口,不会直接跳过"整体"来对"部分"做增删改查。这种组合场景中,推荐API设计方式示例如下:

聚合资源入口独立,避免路径嵌套访问

聚合资源,即两种资源之间存在聚合关系。聚合也是整体与部分的弱包含关系,但整体与部分之间是可分离的,他们可以具有各自的生命周期,部分可以属于多个不同的主体对象,也可以为多个整体对象共享。

例如,机构或角色下包含人员,需要获取机构或角色下的人员的场景,避免做成分别通过机构入口或角色入口找人等重复的具有类似功能的接口:

HTTP Status 说明

在RESTful API设计中,正常和异常情况建议通过HTTP约定的status进行区分,不建议采用所有接口均POST Method调用,永远返回200这种模式。

推荐的常用Http Status说明如下:

HTTP 1.0 Status 详细说明参考

正常响应报文

API调用成功后,返回HTTP 2xx状态码,Response Body直接返回业务数据即可。请求和响应报文建议统一采用JSON格式。

异常响应报文

RESTful API 对于异常报文需要规范和统一,服务端出现异常情况下,需要进行全局拦截,然后将异常信息封装为规范的格式,返回给调用端。

对于后端的异常信息,建议包含编码和消息

结尾

本文是基于学习各路大神们对RESTful 设计相关文章,结合自己设计接口时遇到困惑后的解决方案,收集与总结而成的RESTful API设计约定。部分内容夹杂个人喜好与主观观点,抛砖引玉,希望能为大家设计API带来些许帮助。后如果遇到一些更复杂的场景,欢迎一起沟通。

参考与引用

转载本文需注明出处:RESTful API 设计约定

上一篇 下一篇

猜你喜欢

热点阅读