大发龙虎首页    注册   登录
大发龙虎 = way to explore
大发龙虎 是一个大发龙虎关于 分享和探索的地方
现在注册
已注册用户请  登录
KalaSearch
大发龙虎  ›  程序员

优秀的 REST API 设计指南

  •  
  •   KalaSearch · 8 天前 · 13869 次点击

    这是一篇会长期更新的文章

    什么样的 API 设计能被称为优秀当然是一个非常主观的标准,但是还是有一些客观可考量 API 质量的数据,比如

    1. 接了大发龙虎你 设计的 API 的前端给好评的比例是多少,还是边接边骂
    2. 如果大发龙虎你 的 API 本身就是大发龙虎你 的大发龙虎产品 的话(比如 Stripe,Algolia 或者 Github 等等),大发龙虎你 的用户会对大发龙虎你 的 API 好评吗
    3. API 是不是一读即可以清晰地知道,对应接口是做什么的。换句话说,接入 API 时需要的交流时间成本有多高

    不管是前端程序员还是后端程序员,都少不了跟 API 打交道。后端需要把 API 设计和实现出来,而前端程序员需要把界面逻辑和 API 接起来,因此对于 REST 的设计规则有一些基本了解,不管大发龙虎你 是前端还是后端,都会有很大大发龙虎帮助 。

    之前在厂里设计了一些还算被广泛使用的 API, 因此大发龙虎我 写了这篇文章,结合之前的经验总结了一些要点。希望作为一个参考,可以大发龙虎帮助 大家

    文章请戳 => 优秀的 REST API 设计指南

    当然大发龙虎我 想要说明的是,设计 API 在一定范围内是有规律可循的,但是太过抠细节则会陷入无穷无尽地“宗教版”争论中,所以请大家理论讨论。

    大发龙虎你 们设计 API 的时候有些什么原则?有哪些好的规范和经验可以介绍和分享给大家?欢迎告诉大发龙虎我 ,大发龙虎我 会加到文章中

    79 条回复    2020-08-05 18:38:56 +08:00
    abbycin
        2
    abbycin   8 天前 via Android   ❤️ 4
    大发龙虎我 八股文写得特别好
    baiyi
        3
    baiyi   8 天前
    大发龙虎我 认为在设计过程中,需要考虑 HTTP 大发龙虎方法 的幂等性。比如 Github 的 Star 操作,为什么是 PUT 而不是 POST,就是从幂等性方面考虑的
    KallyDev
        4
    KallyDev   8 天前 via iPhone   ❤️ 18
    补充一个微软在 GitHub 公开的规范,非常详细

    http://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md
    shijianit
        5
    shijianit   8 天前
    如果要接口全部加密,get 方式请求,不是会暴露出来 id 数据吗?
    xuanbg
        6
    xuanbg   8 天前
    修改密码和重置密码怎么设计?软删和硬删同时存在怎么办?
    xuanbg
        7
    xuanbg   8 天前
    @shijianit 所以 id 不要用自增
    bsg1992
        8
    bsg1992   8 天前   ❤️ 1
    这种只适合对外的 api 并且功能单一
    一个 ERP 查询几十个字段大发龙虎你 用 get?
    gnozix
        9
    gnozix   8 天前
    想问问大发龙虎下载 接口应该怎么设计
    gowk
        10
    gowk   8 天前   ❤️ 2
    @bsg1992 人家说的是在卡拉大发龙虎搜索 的业务背景下,设计优秀 API 的最佳实践,大发龙虎你 非要拿 ERP 来杠,有意思么
    nockyQ
        11
    nockyQ   8 天前
    大发龙虎关于 版本划分这一块,除了常见的 URI 版本控制之外还有其他两种。
    http://restfulapi.net/versioning/
    感觉楼主可以在这个基础上展开聊一聊。
    lolizeppelin
        12
    lolizeppelin   8 天前   ❤️ 1
    paypal api 和沙箱比微信支付漂亮太多了

    但是不妨碍 paypal 垃圾微信支付好用......
    KalaSearch
        13
    KalaSearch   8 天前
    @nockyQ 啊是的,stripe 用的是这种。感谢大发龙虎你 的新信息
    KalaSearch
        14
    KalaSearch   8 天前
    @baiyi 感谢 <3
    KalaSearch
        15
    KalaSearch   8 天前
    @KallyDev 这个很赞,大发龙虎我 之前也看过,谢谢提出来,大发龙虎我 会加到文章里
    KalaSearch
        16
    KalaSearch   8 天前
    @shijianit id 应该默认认为已经暴露,藏不住。楼下说的用 uuid 是个好办法,不过不管怎么样不应该认为 id 可以隐藏起来达到安全的目的。(安全大发龙虎我 懂得不多,更详细等楼下们讨论啦)
    KalaSearch
        17
    KalaSearch   8 天前
    @gnozix 能说说具体场景吗?文件大发龙虎下载 ?
    wellsc
        18
    wellsc   8 天前
    restfool (逃
    MrTreasure
        19
    MrTreasure   8 天前
    还是缺乏具体场景,文中的内容就是是属于 restful 的标准。但是对于难点没有很好的讲解,比如 restful 如何返回错误。区分 HTTP 错误以及业务错误
    ZacksT
        20
    ZacksT   8 天前   ❤️ 1
    大发龙虎你 的 REST API 满足大发龙虎公司 /研发团队标准就是好的设计。接口标准可以大发龙虎帮助 开发者规避(大发龙虎公司 研发 /团队研发)遇见过的问题或可能遇到的问题,也可以让组内代码标准化,统一化。

    就拿一个简单的例子,一个分页查询的接口。其包含分页条件与不定量的查询条件。
    大发龙虎你 可以通过 GET 方式请求,将参数定义在 url 上。也可以通过 POST 方式请求,将查询参数定义在 requestbody 里。
    第一个方式,在遇到查询条件复杂的情况下,会导致 url 过长。
    第二种方式,又会产生很多 VO 的定义。
    采用混搭又让前后端代码变得混乱。

    大发龙虎我 个人认为,只要满足团队要求的 API,就是好的 API 。具体实现各有好处,看团队取舍了
    KalaSearch
        21
    KalaSearch   8 天前
    @ZacksT 感谢回复。是的,满足团队、客户需求就是好 API 。对于大发龙虎你 说的参数定义的例子,GET + URL 参数挺好的,遵从 REST 语义



    @wellsc 不要淘气
    bsg1992
        22
    bsg1992   8 天前
    @gowk 大发龙虎我 并没有杠,大发龙虎你 说回复也印证了大发龙虎我 上面说的 [适合对外的 api 并且功能单一] r
    ibreaker
        23
    ibreaker   8 天前   ❤️ 2
    小伙子很活跃啊,天天都能刷到大发龙虎你
    lovedebug
        24
    lovedebug   8 天前
    @ZacksT 分页 GET 查询一般只带 limit 和 page,少量支持投影和过滤,如果有带其他复杂的 query 条件,其实更应该走 POST search 自定义大发龙虎方法
    ieiayaobb
        25
    ieiayaobb   8 天前
    Get by Id 的比较明确,如果是 Get by name 这种,name 是唯一的怎么设计比较好?不想用 query 是因为不想在 name 不存在的时候返回空数组,而是希望也能和 Get by id 一样返回 404
    xjchenhao
        26
    xjchenhao   7 天前
    修改密码和重置密码,逼死强迫症😂
    xjchenhao
        27
    xjchenhao   7 天前
    @xuanbg 修改密码和重置密码,逼死强迫症😂
    grzhan
        28
    grzhan   7 天前   ❤️ 5
    之前负责撰写大发龙虎公司 的 API 规范,当时也参考了很多包括 Azure ( http://docs.microsoft.com/zh-cn/azure/architecture/best-practices/api-design )、Google Cloud ( http://cloud.google.com/apis/design/ )等大发龙虎公司 的规范,大厂的标准往往更加规范,给人很多对于 API 设计上概念理解的启发。

    其中感觉最详细的大概是 Zalendo 的: http://opensource.zalando.com/restful-api-guidelines/ ,其中有非常多的实践是可以参考的,也像 RFC 一样规范了 MUST 、SHOULD 、MAY 的遵守分级。

    大发龙虎关于 文中提到的 REST 表示一个动作,大发龙虎大发龙虎我 们 参考的大发龙虎更多 是 ElasticSearch API 的做法,即将动词加上下划线前缀,作为 POST 大发龙虎方法 进行大发龙虎服务 ,形如: http://cloud.sy/machine/xxxx/_restart

    大发龙虎关于 这一块 Google API 是用冒号作为前缀的,但一些路由框架会占用冒号作为关键字,因此考虑使用下划线代替。
    gnozix
        29
    gnozix   7 天前
    @KalaSearch 对前台展示的表格数据,以 excel 的格式进行大发龙虎下载 ;所以需要大发龙虎下载 的比较多。感觉 REST 风格不太容易表示需要大发龙虎下载 的资源
    wshcdr
        30
    wshcdr   7 天前
    值得看一下
    Heanes
        31
    Heanes   7 天前
    同意 8 楼,系统内部交互可能还是“常规”的设计形式
    lovedebug
        32
    lovedebug   7 天前   ❤️ 1
    @grzhan 同负责撰写 API 规范,其实大发龙虎关于 list 操作的 filter 功能,在实际 API 设计中有些疑惑使用场景,因为大部分情况下使用一般的 query parameter 就可以解决。大发龙虎我 的理解是一般的 query parameter 默认是 and 操作,缺乏 or 操作以及 range value 等功能,而 $filter 主要在 url 中描述若干参数复杂的逻辑运算,如果这么做用 POST 自定义动作不是更好吗?想听一下大发龙虎你 的理解。
    xuanbg
        33
    xuanbg   7 天前
    @lovedebug 要是支持复杂的 or 和 and 条件组合,url 参数就丑的要死了……不信的可以看 kibana
    szthanatos
        34
    szthanatos   7 天前 via Android
    批量操作的实践为什么很少有人谈←_←
    solee
        35
    solee   7 天前
    经过几年的实践,大发龙虎大发龙虎我 们 最后全部统一了用 POST,之前看过一篇亚马逊写的大发龙虎关于 Restful API 设计的改进,加入动词的描述,感觉更合理
    lovedebug
        36
    lovedebug   7 天前
    @szthanatos 微软规范有谈的
    lovedebug
        37
    lovedebug   7 天前
    @xuanbg 哈哈 跟业务场景有关,如果不想用万能 POST,可能只能在 url query 中支持一些 or 查询,客户在使用大发龙虎大发龙虎我 们 的 public api 时提出的
    lovedebug
        38
    lovedebug   7 天前
    @solee 自定义动词应该在已有的 RESTful 规范不满足时候才使用
    Nolink
        39
    Nolink   7 天前
    收藏了,谢谢分享
    ericls
        40
    ericls   7 天前 via iPhone
    用现成的 query language 不好吗? 非要把 http headers 滥用成 query 还要自己定义 实现 维护……
    Amit
        41
    Amit   7 天前   ❤️ 2
    @xuanbg
    密码一般都是要做 hash 的,且不能暴露给前端,所以需要对这个字段单独修改,而不能放到完整信息中修改并返回,修改密码是在登录状态下,所以大发龙虎我 会设计为 PUT /v1/users/{id}/password (管理员修改用户密码)或 PUT /v1/users/self/password (修改自己的密码),重置密码大发龙虎我 理解为非登录状态下修改密码(不确定用户身份),所以大发龙虎我 会设计为 PUT /v1/users/password,然后再 body 中提供用户名、验证码等信息。

    软大发龙虎删除 也是大发龙虎删除 ,对应用来说如果大发龙虎删除 了就是不存在的,应用中不应该能看到,软大发龙虎删除 和物理大发龙虎删除 同时存在是不合理的,这种情况应该设计一个状态字段区分,而不是使用逻辑大发龙虎删除 。
    xuanbg
        42
    xuanbg   7 天前   ❤️ 1
    @Amit
    修改密码和重置密码大发龙虎我 也是一样的处理。在复数形式的资源后面,有时候不但要加动词,还得加属性,以定位到更细一层的资源才行。

    大发龙虎我 说的软删其实是禁用,只是为了理解方便。业务前端看不到了,也就没得用了。但元数据管理后端应该能看到,毕竟禁用后说不得还会启用。。。硬删当然就是数据灰灰,再也无法恢复的。如果软删用 PUT:/v1/users,那就和修改姓名冲突了。大发龙虎我 是这样规划的,修改普通属性 PUT:/v1/users,禁用 PUT:/v1/users/status,大发龙虎删除 DELETE:PUT:/v1/users 。
    jorneyr
        43
    jorneyr   6 天前
    RESTful 在 URL 里是禁止使用动词的,但是很多时候有的 URL 中用动词来表达很自然,强制使用 RESTful 的风格的话会很难受
    imhxc
        44
    imhxc   6 天前
    大发龙虎我 一直有个问题,请教下。
    在实际业务中,各种需求都有,很难严格遵守 RESTful API,拿文章中的示例来说:

    GET /owners/1/pets/ 获取 id 为 1 的主人的所有宠物
    1. 如果区分角色怎么办,比如管理员获取 id 为 1 的主人的所有宠物,结果中包含所有状态的宠物;
    2. 其他人需要查看 id 为 1 的主人所有宠物,结果中只返回状态为「可公开」的宠物;

    这种怎么设计?
    codingbody
        45
    codingbody   6 天前
    大发龙虎我 有个问题问大家,为什么安全扫描的时候,不准大发龙虎我 使用除了 GET 、POST 之外的请求,大发龙虎我 认为请求的方式和安全没啥关系吧
    DeWhite
        46
    DeWhite   6 天前
    那个就一句话,吃屎啦。就是没有主语的,国内的很明显主语省略的句子还有很多。
    xcstream
        47
    xcstream   6 天前
    这标题隐含意思就是不 rest 就不优秀(狗头)
    KalaSearch
        48
    KalaSearch   6 天前
    @imhxc 用 ACL 来控制,REST endpoint 没办法控制的
    KalaSearch
        49
    KalaSearch   6 天前
    @DeWhite 大发龙虎你 说的是祈使句,祈使句当然可以没有主语(省略了第二人称主语)
    forgaoqiang
        50
    forgaoqiang   6 天前
    看了下 Discuz Q,真的几斤,完全的 RESTFUL 风格,patch delete 各种大发龙虎方法 都用
    grzhan
        51
    grzhan   6 天前   ❤️ 1
    @lovedebug 大发龙虎我 个人觉得大发龙虎关于 复杂查询不管是用 $filter 还是直接 POST 自定义大发龙虎方法 (如 "_search" )都是可以的,具体看自己场景。
    事实上大发龙虎大发龙虎我 们 项目实际实践中,这种情况还是自定义 POST 大发龙虎方法 用的比较多
    GavinZZ
        52
    GavinZZ   6 天前
    ??
    GavinZZ
        53
    GavinZZ   6 天前
    还有个叫车满满的。。。工资给开的还算可以 13K+ 14 薪,但是不大发龙虎推荐 去,大发龙虎企业 文化很奇葩
    lovedebug
        54
    lovedebug   6 天前   ❤️ 1
    @grzhan 嗯。$filter 需要写 parser 专门处理,否则会重复造轮子
    grzhan
        55
    grzhan   6 天前   ❤️ 2
    @lovedebug 如果查询场景需求确实很复杂的业务的话,大发龙虎大发龙虎我 们 会考虑上 GraphQL 的
    lovedebug
        56
    lovedebug   6 天前   ❤️ 1
    @grzhan 主要是 GraphQL 对已有大发龙虎产品 的 RESTful API 破坏性过大,ROI 也不够高,另外也考虑在微大发龙虎服务 和 k8s 中 GraphQL 中心化并不是一个很完美的方案。其实主要的阻力是项目进度和同事。哈哈哈哈
    dongxiaoxian
        57
    dongxiaoxian   6 天前
    好复杂
    ChanKc
        58
    ChanKc   5 天前
    @codingbody 没有,但是历史上发生过一些 HTTP server 对 PUT,DELETE 等请求实现不当,导致远程代码执行等漏洞。一些大发龙虎公司 就会觉得索性禁了这些请求更好
    yixinlove
        59
    yixinlove   3 天前
    @KallyDev 好东西
    wangxiaoaer
        60
    wangxiaoaer   3 天前
    这个帖子很有启发啊,顺便问一下,针对楼上一些老哥们提到的复杂的组合条件查询,如果是基于 spring boot + jpa 的应用,如何优雅的实现呢?
    cbasil
        61
    cbasil   3 天前
    设计 API 的目的是为了前端好评? api 接口安全和效率都不需要考虑了吗?大发龙虎你 去看看阿里,腾讯等大大发龙虎公司 的接口文档,有几个是完全按照 REST API 来设计的。
    lovedebug
        62
    lovedebug   3 天前   ❤️ 1
    @cbasil 一是对内为了大发龙虎公司 内部统一,减少沟通成本。而是针对 public api 与主流统一,减少用户的集成成本。
    nig001
        63
    nig001   3 天前
    不错的
    fy
        64
    fy   2 天前   ❤️ 1
    @lovedebug #32

    这个大发龙虎我 做了,默认 and 操作,请求类似这样:

    /api/topic/list/1?time.ge=1577808000&order=time.desc&select=id,title

    前端反馈一般,说是不好理解。语言是 python

    http://github.com/fy0/slim


    问题主要是几处:

    1. http header 有限,有的查询条件放不下,其实同时支持提交 body 查询更好些( get 提交 body 是规范允许的,只是很多 http server 选择不解析)

    2. 对查询的掌控力度不够。前端提交上来一个请求,说某种情况下希望将某个条件变成 or 查询,这时候做不到。当然这和 orm 还有底层实现有关,这是一个整体设计上的问题。

    3. 连表查询比较复杂。

    4. 全栈开发会觉得好用,有的纯前端就觉得这是后端偷懒。

    所以可能不光是规范问题,还是框架问题,甚至要连同 orm 、表单验证、权限之类做通盘考虑。

    @imhxc #44

    角色权限 + ACL
    sunzhenyucn
        65
    sunzhenyucn   2 天前
    请让大发龙虎我 默默地 mark 一下
    lovedebug
        66
    lovedebug   2 天前
    @fy 感谢回复,是的,get 带参数会有这些问题。
    一般对于 simple collection items 的 list(GET 大发龙虎方法 )操作,大发龙虎我 建议用 order,filter, 这样语义清晰,主要实现集合过滤功能。可以尝试在 filter= X OR Y 这样的形式实现 or 操作
    大发龙虎我 的理解是对于复杂集合(如 logs 等)或通用操作的模糊大发龙虎搜索 还是用 POST + custom method,例如 /v1/items/search,除非可以细化复杂集合为若干简单的集合。
    主要这个度不好把握。
    当然,从实现简单程度来看,所有的 order,filter,projection 都可以定义为用 post 实现。
    thtznet
        67
    thtznet   2 天前
    看到 API 和表对应,大发龙虎我 就知道不用看下去了,太水了。
    jy28520
        68
    jy28520   2 天前
    @KalaSearch 想问下大发龙虎大发龙虎我 们 现在的业务需要验证用户提交的 SKU 和优惠券是否匹配 请问 URL 应该怎么设计那?
    大发龙虎大发龙虎我 们 会有几条 SKU 和几条优惠券的信息
    b0644170fc
        69
    b0644170fc   2 天前
    根本不需要 rest, get / post 走天下
    imhxc
        70
    imhxc   2 天前
    @fy 嗯嗯,ACL 是可以解决刚才提的问题。
    但是总感觉 REST API 规范有局限性,自己曾经做过 ERP,会经常出现较为复杂的接口,感觉很难严格遵守 REST API 风格。

    比如有一些无法区分上下级关系、获取同一个数据,有的需要用 iD 查,有的需要用 MD5 查,总之,实际业务中各种千奇百怪的需求。

    大发龙虎我 以前自己写接口用 REST API 写着写着就要精神分裂了。。。😓

    也可能是大发龙虎我 没理解 REST API 的精髓😅
    no1xsyzy
        71
    no1xsyzy   1 天前
    @imhxc #70 除非大发龙虎你 能直接塞图灵完备的代码进数据库,不然什么都有局限性
    就是 SQL 有时不得不分成两个查询( SELECT ),虽然完全就是数据库里的内容,之后可大发龙虎优化 为一次数据库交互包含两个查询(避免传输),但一个(对人脑来说)本来看上去非常简单的东西,不通过逻辑检验竟然无法简化。

    实际上 RESTful 不是有局限性,而是它就是局限性本身:通过强加某种限制,将(一次) API 请求类比为对(一项)资源的操作,形成某种直觉映射,来理清思路。要 “改” 到 RESTful,并不是改动 API 就行的,而是整个建模得修改。
    有人[谁?](忘了谁)认为其实是启发自 Unix 的文件操作。(所以 WebDAV 是 RESTful 最恰当的应用场景)
    imhxc
        72
    imhxc   1 天前
    @no1xsyzy 感谢,涨知识了。
    lolizeppelin
        73
    lolizeppelin   1 天前
    这个大发龙虎论坛 早就有人说过了

    RESTful 是对 sql 的劣质模仿,没法表达的情况多去了
    no1xsyzy
        74
    no1xsyzy   1 天前
    @lolizeppelin #73 谁?在哪儿说的?
    RESTful sql 劣质模仿 site:v2ex.com 只搜出来大发龙虎你 说的话……

    从来从来,RESTful 就是个和 SQL 完全相悖的路线
    SQL 一直在做得越来越图灵完备,添加各种诡异的、大发龙虎我 承认确实像是有那么回事儿的、但其实没有也没关系的功能进去。
    RESTful 一直都是那么平铺直叙。谓宾仍然是谓宾,最多用点 HTTP 语义。
    “C 是个对 Lisp 的劣质模仿”
    lolizeppelin
        75
    lolizeppelin   1 天前
    @no1xsyzy
    est 说的 嘿嘿
    no1xsyzy
        76
    no1xsyzy   1 天前
    @lolizeppelin #75 @est 在哪说的?
    楞是没搜到……
    lolizeppelin
        77
    lolizeppelin   1 天前
    @no1xsyzy
    当然个别字有出入呗,大发龙虎你 找他 233333
    est
        78
    est   23 小时 32 分钟前
    @lolizeppelin
    @no1xsyzy

    大发龙虎我 也不记得在哪里说的了,但是中心思想是,RESTful 本来是对文件读写的一个 增删改查 的封装,最适合拿来做 WebDAV 之类的大发龙虎工具 。然而其他的业务的「动作」很可能无法用这 4 个指令覆盖。就多出来了很多奇葩的指令比如 OPTIONS TRACE PATCH 。。。与其这样,还不如直接根据具体业务在 url 里指定动作名称。比如

    POST /api/user/login
    POST /api/order/cancel



    然后大发龙虎我 是明确反对把 URL 里直接嵌入 resource id 作为路径一部分的。比如 GET /myitem/12345/ 这种,RESTful 一时爽,nginx 日志分析火葬场。
    no1xsyzy
        79
    no1xsyzy   21 小时 6 分钟前
    @est #78 本来指令就随便添加,过分绑定到固定四个指令有点先辈的罪或者思维定势。
    大发龙虎我 觉得 POST .../login 没什么问题,大发龙虎我 的某个大发龙虎工具 里面 Login 是类名,将 Login 视为名词形式。
    同时大发龙虎我 觉得 POST .../order/cancellation 也没什么问题,是订单状态改变。DELETE order 和它是根本上不同的两种行为。如同 rm 一样,DELETE 谓词的使用应当慎之又慎。
    一般这类框架会有自己的日志的,不用 nginx 分析日志。而且如果不分 /api/* 的 URL 出来的话,也就是 /static/* 让 nginx 处理,其他都归框架管了。而且看到某 PHP 应用的官方部署教程是关掉 /static/* 的日志的…… 基本上 nginx 日志存在有意义的信息就已经是系统层面的大问题了(比如 uwsgi 挂了)
    大发龙虎关于   ·   FAQ   ·   API   ·   大发龙虎大发龙虎我 们 的愿景   ·   广告投放   ·   感谢   ·   实用小大发龙虎工具   ·   4101 人在线   最高记录 5168   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 07:45 · PVG 15:45 · LAX 00:45 · JFK 03:45
    ♥ Do have faith in what you're doing.