关于api设计的一些思考-restful理想主义与商业需求的无解冲突

出发点

本人最近负责部门930的一个核心需求,设计阶段主要负责数据库和api的设计。数据库的重要性无需多说,大家的设计理念也比较一致,评审很顺利。但api的评审意见却五花八门,着实让我惊了一番。

项目的api设计标准已经发布,是restful风格。但是项目的历史api中符合restful规范的寥寥可数。其次,评审过程中很多同事连restful风格都不了解,其对api设计的重要性看法可见一斑。

基于这种不重视api设计的普遍现象和本人极度重视api设计的观念冲突,认真梳理以下api的设计范式和应用场景是很有必要的。

正文

问题

如下是我们项目的一个api。

这个api看似是遵循restful设计的。但是其中有几个非常严重的问题:

  1. 前缀apps是一个无用的名词,软件中app几乎可以代表一切,这里只会增加api的复杂性
  2. asset/asset_view/asset_view不明所以,不知要表意什么。并且asset_view重复,同样只会增加api的复杂性
  3. on_list像是名词又像是动词,而且list在此处只是一个动作,不是一个资源,api语义不明。
  4. 获取资产信息采用post方法。

做了什么

此次需求设计注意到了上次的问题,需要避免。

常见的api设计规范有三:restful,rpc和graphql。在此不会详细描述三种规范,只通过例子表述其理念。

restful

大名鼎鼎的restful风格。其核心为资源,即如何从api操作中抽象出资源。并通过http方法(get,post,patch,put,delete等)表示对资源的操作。restful从03年提出,到10年之后风靡,我人为其和面向对象蓬勃发展有着很大的关系。如果把资源看作对象,资源的操作看作对象的方法,那么就是妥妥的面向对象在交互方面的实现。

github就是这方面的典范。如下面这个例子:

/repos/{owner}/{repo}/branches。通过路径参数来表示某人仓库资源下面的分支资源。通过查询参数来附加limit等属性。其好处是见名知义。例如我直接在路径后面加上某个commit即可以直接跳转到对应的commit,甚至不用翻页和和跳到对应的搜索框搜索。

更多信息请参考:GitHub REST API - GitHub Docs

rpc

rpc的api设计风格名声不如restful大,但是每个没有规范的开发者设计时多多少少会有此风格的影子。其在api路径中表示定义的动作,很符合我们用api表示一个操作的惯性思维。rpc的设计理念为命名空间/资源类型/具体操作。如下面为slack.com这个空间api资源类型下对会话得到归档操作

其特点是设计理念简单(但是随着需求扩展和功能增加,要设计好资源,动作并不简单),也很符合人们描述api需求的直觉。也是现在最常见的没有规范指导下api最贴近的风格。

更多信息请参考:conversations.archive method | Slack

graphql

graphql是facebook提出的api查询语言,在面对一个复杂的图状数据查询的时候很有效率。其理念为将图状结构的类型和对应key传送给后端,后端返回相同图状结构的值响应

如下图即为官网的一个例子。定义好Query和User的类型和数据key,返回图状数据响应。

更多信息请参考:GraphQL 入门 | GraphQL

确认设计

从评审设计到确认设计经过两个步骤:

第一个过程是按照项目规范restful设计的api。以下为一个样例: 此需求为根据port和service查询对应的资产l。如查询port为22,service为ssh的资产。

url:inventory/ports/{port_id}/{services}/{service_id}/assets

method:GET

auth_required: true

timeout:30s

request

path params:
  port_id: 
    type: int
    required: true
    example: 36
		range: 1~65535
  service_id:
    type: str
    required: true
    example: ssh

query params:
  limit:
    type: int
    required: false
    default: 50
    enum:
      - 10
      - 20
      - 50
      - 100
      - 200
      - 500
  offset:
    type: int
    required: false
    default: 0

response

{
  "success": true | false,
  "total": 100,
  "data": [
    {
      "asset_name": "xxx",
      "ip": "192.168.0.1",
      "owner": "xxx",
    }
  ]
}

第二个过程是rpc的设计。从restful转为rpc设计,最主要的原因是因为restful设计需要明确资源,对于频繁变动的商业需求来说,开发工作量会很大。基于这方面考量,最终按照rpc的规范重新设计了api。

设计规范为统一使用POST请求方法,通过.action后缀来实现动作表述,目前只支持get,create,delete,upate,aggregate。具体api设计如下:

url:api/inventory/ports-services.assets.get

method:POST

auth_required: true

timeout:30s

reqeust

post body:
  port_id: 
    type: int
    required: true
    example: 36
		range: 1~65535
  service_id:
    type: str
    required: true
    example: ssh
  limit:
    type: int
    required: false
    default: 50
    enum:
      - 10
      - 20
      - 50
      - 100
      - 200
      - 500
  offset:
    type: int
    required: false
    default: 0

response

同restful设计的响应。

后续思考

restful的好看与不实用

在这次设计之前,本人一直人为restful就是神,无脑推崇restful反而被restful规范束缚。现在想来,restful对于部分需求(甚至可以说大多数商业需求)并不合适。

对比github的这种完美的github设计会发现restful场景限制。github是提供工具供我们使用的。资源相对好划分,需求变动也不大。如仓库,分支,提交的资源,不可能频繁变动,github定位为代码仓库,那么主要这次hi资源的CURD,和restful规范十分契合。但是回到我们的商业需求场景。用户需求千变万化。我们不可能一直提取资源,提取资源。甚至很多场景很难提取资源。

最后,restful本身只是一个风格,属于非常理想化的状态,算不上严谨的规范。比如一个资产的清点,报废,维修等等,都抽象称资源,并且需求频繁变动,设计起来是复杂费力的。

rpc的简单与复杂

rpc设计看似简单。但是对于对于一般场景来说,命名空间和资源类型十分固定。那么通过后缀来实现几乎api的所有语义,并不简单。例如github的 repos/{owner}/{repo}/branches分支apirpc的设计很可能会设计为repos.owner.repo.branches.get。这样和restful又有什么区别呢 。从rpc领域和行为的角度出发,个人人为设计为repos.branches.get比较好。

rpc的api设计可以说比restful的定义还要模糊。所以每个项目的api都要有自身一致的规范。比如资源的层级是多少,定义的行为有哪些等等。

初看graphql几乎可以覆盖所有的需求。但是grapql有很多明显的缺点。只能在某些特定情况下使用。

  1. 首先很多简单的需求通过graphql定义会过于复杂。获取登录验证码想必没有人愿意通过graphql来查询。
  2. 数据库必须以图状设计才能体现graphql的优势。要不然后端对于资源的逻辑处理会抓狂。
  3. 图状数据存储和图状api设计,查询子层节点或者重组结构会十分困难。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值