什么样的 API 设计能被称为优秀当然是一个非常主观的标准,但是还是有一些客观可考量 API 质量的数据,比如
不管是前端程序员还是后端程序员,都少不了跟 API 打交道。后端需要把 API 设计和实现出来,而前端程序员需要把界面逻辑和 API 接起来,因此对于 REST 的设计规则有一些基本了解,不管你是前端还是后端,都会有很大帮助。
之前在厂里设计了一些还算被广泛使用的 API, 因此我写了这篇文章,结合之前的经验总结了一些要点。希望作为一个参考,可以帮助大家
当然我想要说明的是,设计 API 在一定范围内是有规律可循的,但是太过抠细节则会陷入无穷无尽地“宗教版”争论中,所以请大家理论讨论。
![]() | 1 KalaSearch OP |
2 abbycin 2020-07-29 07:24:49 +08:00 via Android ![]() 我八股文写得特别好 |
![]() | 3 baiyi 2020-07-29 07:30:25 +08:00 ![]() 我认为在设计过程中,需要考虑 HTTP 方法的幂等性。比如 Github 的 Star 操作,为什么是 PUT 而不是 POST,就是从幂等性方面考虑的 |
![]() | 4 KallyDev 2020-07-29 07:31:05 +08:00 via iPhone ![]() |
![]() | 5 shijianit 2020-07-29 08:21:40 +08:00 如果要接口全部加密,get 方式请求,不是会暴露出来 id 数据吗? |
![]() | 6 xuanbg 2020-07-29 08:22:37 +08:00 修改密码和重置密码怎么设计?软删和硬删同时存在怎么办? |
8 bsg1992 2020-07-29 08:43:48 +08:00 ![]() 这种只适合对外的 api 并且功能单一 一个 ERP 查询几十个字段你用 get? |
9 gnozix 2020-07-29 08:51:17 +08:00 想问问下载接口应该怎么设计 |
![]() | 11 nockyQ 2020-07-29 09:12:50 +08:00 |
12 lolizeppelin 2020-07-29 09:36:29 +08:00 ![]() paypal api 和沙箱比微信支付漂亮太多了 但是不妨碍 paypal 垃圾微信支付好用...... |
![]() | 14 KalaSearch OP @baiyi 感谢 <3 |
![]() | 15 KalaSearch OP @KallyDev 这个很赞,我之前也看过,谢谢提出来,我会加到文章里 |
![]() | 16 KalaSearch OP @shijianit id 应该默认认为已经暴露,藏不住。楼下说的用 uuid 是个好办法,不过不管怎么样不应该认为 id 可以隐藏起来达到安全的目的。(安全我懂得不多,更详细等楼下们讨论啦) |
![]() | 17 KalaSearch OP @gnozix 能说说具体场景吗?文件下载? |
![]() | 18 wellsc 2020-07-29 09:53:32 +08:00 restfool (逃 |
![]() | 19 MrTreasure 2020-07-29 10:25:34 +08:00 还是缺乏具体场景,文中的内容就是是属于 restful 的标准。但是对于难点没有很好的讲解,比如 restful 如何返回错误。区分 HTTP 错误以及业务错误 |
20 ZacksT 2020-07-29 10:58:18 +08:00 ![]() 你的 REST API 满足公司 /研发团队标准就是好的设计。接口标准可以帮助开发者规避(公司研发 /团队研发)遇见过的问题或可能遇到的问题,也可以让组内代码标准化,统一化。 就拿一个简单的例子,一个分页查询的接口。其包含分页条件与不定量的查询条件。 你可以通过 GET 方式请求,将参数定义在 url 上。也可以通过 POST 方式请求,将查询参数定义在 requestbody 里。 第一个方式,在遇到查询条件复杂的情况下,会导致 url 过长。 第二种方式,又会产生很多 VO 的定义。 采用混搭又让前后端代码变得混乱。 我个人认为,只要满足团队要求的 API,就是好的 API 。具体实现各有好处,看团队取舍了 |
![]() | 21 KalaSearch OP |
![]() | 23 ibreaker 2020-07-29 11:57:46 +08:00 ![]() 小伙子很活跃啊,天天都能刷到你 |
![]() | 24 lovedebug 2020-07-29 12:40:40 +08:00 @ZacksT 分页 GET 查询一般只带 limit 和 page,少量支持投影和过滤,如果有带其他复杂的 query 条件,其实更应该走 POST search 自定义方法 |
25 ieiayaobb 2020-07-29 13:09:32 +08:00 Get by Id 的比较明确,如果是 Get by name 这种,name 是唯一的怎么设计比较好?不想用 query 是因为不想在 name 不存在的时候返回空数组,而是希望也能和 Get by id 一样返回 404 |
![]() | 26 xjchenhao 2020-07-29 16:27:06 +08:00 修改密码和重置密码,逼死强迫症 |
![]() | 28 grzhan 2020-07-29 17:27:59 +08:00 ![]() 之前负责撰写公司的 API 规范,当时也参考了很多包括 Azure ( https://docs.microsoft.com/zh-cn/azure/architecture/best-practices/api-design )、Google Cloud ( https://cloud.google.com/apis/design/ )等公司的规范,大厂的标准往往更加规范,给人很多对于 API 设计上概念理解的启发。 其中感觉最详细的大概是 Zalendo 的: https://opensource.zalando.com/restful-api-guidelines/ ,其中有非常多的实践是可以参考的,也像 RFC 一样规范了 MUST 、SHOULD 、MAY 的遵守分级。 关于文中提到的 REST 表示一个动作,我们参考的更多是 ElasticSearch API 的做法,即将动词加上下划线前缀,作为 POST 方法进行服务,形如: http://cloud.sy/machine/xxxx/_restart 关于这一块 Google API 是用冒号作为前缀的,但一些路由框架会占用冒号作为关键字,因此考虑使用下划线代替。 |
29 gnozix 2020-07-29 18:01:56 +08:00 @KalaSearch 对前台展示的表格数据,以 excel 的格式进行下载;所以需要下载的比较多。感觉 REST 风格不太容易表示需要下载的资源 |
30 wshcdr 2020-07-29 19:10:54 +08:00 值得看一下 |
31 Heanes 2020-07-29 19:16:17 +08:00 同意 8 楼,系统内部交互可能还是“常规”的设计形式 |
![]() | 32 lovedebug 2020-07-30 00:06:28 +08:00 ![]() @grzhan 同负责撰写 API 规范,其实关于 list 操作的 filter 功能,在实际 API 设计中有些疑惑使用场景,因为大部分情况下使用一般的 query parameter 就可以解决。我的理解是一般的 query parameter 默认是 and 操作,缺乏 or 操作以及 range value 等功能,而 $filter 主要在 url 中描述若干参数复杂的逻辑运算,如果这么做用 POST 自定义动作不是更好吗?想听一下你的理解。 |
34 szthanatos 2020-07-30 09:01:18 +08:00 via Android 批量操作的实践为什么很少有人谈←_← |
![]() | 35 solee 2020-07-30 09:10:45 +08:00 经过几年的实践,我们最后全部统一了用 POST,之前看过一篇亚马逊写的关于 Restful API 设计的改进,加入动词的描述,感觉更合理 |
![]() | 36 lovedebug 2020-07-30 09:42:19 +08:00 @szthanatos 微软规范有谈的 |
![]() | 37 lovedebug 2020-07-30 09:43:30 +08:00 @xuanbg 哈哈 跟业务场景有关,如果不想用万能 POST,可能只能在 url query 中支持一些 or 查询,客户在使用我们的 public api 时提出的 |
![]() | 39 Nolink 2020-07-30 09:58:50 +08:00 收藏了,谢谢分享 |
![]() | 40 ericls 2020-07-30 10:11:56 +08:00 via iPhone 用现成的 query language 不好吗? 非要把 http headers 滥用成 query 还要自己定义 实现 维护…… |
![]() | 41 Amit 2020-07-30 10:52:53 +08:00 ![]() @xuanbg 密码一般都是要做 hash 的,且不能暴露给前端,所以需要对这个字段单独修改,而不能放到完整信息中修改并返回,修改密码是在登录状态下,所以我会设计为 PUT /v1/users/{id}/password (管理员修改用户密码)或 PUT /v1/users/self/password (修改自己的密码),重置密码我理解为非登录状态下修改密码(不确定用户身份),所以我会设计为 PUT /v1/users/password,然后再 body 中提供用户名、验证码等信息。 软删除也是删除,对应用来说如果删除了就是不存在的,应用中不应该能看到,软删除和物理删除同时存在是不合理的,这种情况应该设计一个状态字段区分,而不是使用逻辑删除。 |
![]() | 42 xuanbg 2020-07-30 12:01:16 +08:00 ![]() @Amit 修改密码和重置密码我也是一样的处理。在复数形式的资源后面,有时候不但要加动词,还得加属性,以定位到更细一层的资源才行。 我说的软删其实是禁用,只是为了理解方便。业务前端看不到了,也就没得用了。但元数据管理后端应该能看到,毕竟禁用后说不得还会启用。。。硬删当然就是数据灰灰,再也无法恢复的。如果软删用 PUT:/v1/users,那就和修改姓名冲突了。我是这样规划的,修改普通属性 PUT:/v1/users,禁用 PUT:/v1/users/status,删除 DELETE:PUT:/v1/users 。 |
43 jorneyr 2020-07-30 16:09:28 +08:00 RESTful 在 URL 里是禁止使用动词的,但是很多时候有的 URL 中用动词来表达很自然,强制使用 RESTful 的风格的话会很难受 |
44 imhxc 2020-07-30 18:19:45 +08:00 我一直有个问题,请教下。 在实际业务中,各种需求都有,很难严格遵守 RESTful API,拿文章中的示例来说: GET /owners/1/pets/ 获取 id 为 1 的主人的所有宠物 1. 如果区分角色怎么办,比如管理员获取 id 为 1 的主人的所有宠物,结果中包含所有状态的宠物; 2. 其他人需要查看 id 为 1 的主人所有宠物,结果中只返回状态为「可公开」的宠物; 这种怎么设计? |
![]() | 45 codingbody 2020-07-30 18:22:52 +08:00 我有个问题问大家,为什么安全扫描的时候,不准我使用除了 GET 、POST 之外的请求,我认为请求的方式和安全没啥关系吧 |
![]() | 46 DeWhite 2020-07-30 18:59:43 +08:00 那个就一句话,吃屎啦。就是没有主语的,国内的很明显主语省略的句子还有很多。 |
![]() | 47 xcstream 2020-07-30 20:21:36 +08:00 这标题隐含意思就是不 rest 就不优秀(狗头) |
![]() | 48 KalaSearch OP @imhxc 用 ACL 来控制,REST endpoint 没办法控制的 |
![]() | 49 KalaSearch OP @DeWhite 你说的是祈使句,祈使句当然可以没有主语(省略了第二人称主语) |
![]() | 50 forgaoqiang 2020-07-31 12:16:24 +08:00 看了下 Discuz Q,真的几斤,完全的 RESTFUL 风格,patch delete 各种方法都用 |
![]() | 51 grzhan 2020-07-31 13:46:45 +08:00 ![]() @lovedebug 我个人觉得关于复杂查询不管是用 $filter 还是直接 POST 自定义方法(如 "_search" )都是可以的,具体看自己场景。 事实上我们项目实际实践中,这种情况还是自定义 POST 方法用的比较多 |
52 GavinZZ 2020-07-31 13:52:12 +08:00 ?? |
53 GavinZZ 2020-07-31 13:52:38 +08:00 还有个叫车满满的。。。工资给开的还算可以 13K+ 14 薪,但是不推荐去,企业文化很奇葩 |
![]() | 56 lovedebug 2020-07-31 14:20:31 +08:00 ![]() @grzhan 主要是 GraphQL 对已有产品的 RESTful API 破坏性过大,ROI 也不够高,另外也考虑在微服务和 k8s 中 GraphQL 中心化并不是一个很完美的方案。其实主要的阻力是项目进度和同事。哈哈哈哈 |
![]() | 57 dongxiaoxian 2020-07-31 15:09:26 +08:00 好复杂 |
58 ChanKc 2020-07-31 18:57:19 +08:00 @codingbody 没有,但是历史上发生过一些 HTTP server 对 PUT,DELETE 等请求实现不当,导致远程代码执行等漏洞。一些公司就会觉得索性禁了这些请求更好 |
60 wangxiaoaer 2020-08-03 09:23:35 +08:00 这个帖子很有启发啊,顺便问一下,针对楼上一些老哥们提到的复杂的组合条件查询,如果是基于 spring boot + jpa 的应用,如何优雅的实现呢? |
61 cbasil 2020-08-03 09:31:29 +08:00 设计 API 的目的是为了前端好评? api 接口安全和效率都不需要考虑了吗?你去看看阿里,腾讯等大公司的接口文档,有几个是完全按照 REST API 来设计的。 |
![]() | 62 lovedebug 2020-08-03 09:53:26 +08:00/span> ![]() @cbasil 一是对内为了公司内部统一,减少沟通成本。而是针对 public api 与主流统一,减少用户的集成成本。 |
63 nig001 2020-08-03 14:43:53 +08:00 不错的 |
![]() | 64 fy 2020-08-04 01:38:35 +08:00 ![]() @lovedebug #32 这个我做了,默认 and 操作,请求类似这样: /api/topic/list/1?time.ge=1577808000&order=time.desc&select=id,title 前端反馈一般,说是不好理解。语言是 python https://github.com/fy0/slim 问题主要是几处: 1. http header 有限,有的查询条件放不下,其实同时支持提交 body 查询更好些( get 提交 body 是规范允许的,只是很多 http server 选择不解析) 2. 对查询的掌控力度不够。前端提交上来一个请求,说某种情况下希望将某个条件变成 or 查询,这时候做不到。当然这和 orm 还有底层实现有关,这是一个整体设计上的问题。 3. 连表查询比较复杂。 4. 全栈开发会觉得好用,有的纯前端就觉得这是后端偷懒。 所以可能不光是规范问题,还是框架问题,甚至要连同 orm 、表单验证、权限之类做通盘考虑。 @imhxc #44 角色权限 + ACL |
![]() | 65 sunzhenyucn 2020-08-04 04:42:12 +08:00 请让我默默地 mark 一下 |
![]() | 66 lovedebug 2020-08-04 09:35:07 +08:00 @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 实现。 |
67 thtznet 2020-08-04 11:15:03 +08:00 看到 API 和表对应,我就知道不用看下去了,太水了。 |
68 jy28520 2020-08-04 11:22:55 +08:00 @KalaSearch 想问下我们现在的业务需要验证用户提交的 SKU 和优惠券是否匹配 请问 URL 应该怎么设计那? 我们会有几条 SKU 和几条优惠券的信息 |
![]() | 69 b0644170fc 2020-08-04 11:36:51 +08:00 根本不需要 rest, get / post 走天下 |
70 imhxc 2020-08-04 14:24:18 +08:00 @fy 嗯嗯,ACL 是可以解决刚才提的问题。 但是总感觉 REST API 规范有局限性,自己曾经做过 ERP,会经常出现较为复杂的接口,感觉很难严格遵守 REST API 风格。 比如有一些无法区分上下级关系、获取同一个数据,有的需要用 iD 查,有的需要用 MD5 查,总之,实际业务中各种千奇百怪的需求。 我以前自己写接口用 REST API 写着写着就要精神分裂了。。。 也可能是我没理解 REST API 的精髓 |
![]() | 71 no1xsyzy 2020-08-05 11:00:25 +08:00 ![]() @imhxc #70 除非你能直接塞图灵完备的代码进数据库,不然什么都有局限性 就是 SQL 有时不得不分成两个查询( SELECT ),虽然完全就是数据库里的内容,之后可优化为一次数据库交互包含两个查询(避免传输),但一个(对人脑来说)本来看上去非常简单的东西,不通过逻辑检验竟然无法简化。 实际上 RESTful 不是有局限性,而是它就是局限性本身:通过强加某种限制,将(一次) API 请求类比为对(一项)资源的操作,形成某种直觉映射,来理清思路。要 “改” 到 RESTful,并不是改动 API 就行的,而是整个建模得修改。 有人[谁?](忘了谁)认为其实是启发自 Unix 的文件操作。(所以 WebDAV 是 RESTful 最恰当的应用场景) |
73 lolizeppelin 2020-08-05 13:13:45 +08:00 这个论坛早就有人说过了 RESTful 是对 sql 的劣质模仿,没法表达的情况多去了 |
![]() | 74 no1xsyzy 2020-08-05 14:54:39 +08:00 @lolizeppelin #73 谁?在哪儿说的? RESTful sql 劣质模仿 site:v2ex.com 只搜出来你说的话…… 从来从来,RESTful 就是个和 SQL 完全相悖的路线 SQL 一直在做得越来越图灵完备,添加各种诡异的、我承认确实像是有那么回事儿的、但其实没有也没关系的功能进去。 RESTful 一直都是那么平铺直叙。谓宾仍然是谓宾,最多用点 HTTP 语义。 “C 是个对 Lisp 的劣质模仿” |
75 lolizeppelin 2020-08-05 15:12:12 +08:00 @no1xsyzy est 说的 嘿嘿 |
![]() | 76 no1xsyzy 2020-08-05 15:16:09 +08:00 |
77 lolizeppelin 2020-08-05 15:17:08 +08:00 @no1xsyzy 当然个别字有出入呗,你找他 233333 |
![]() | 78 est 2020-08-05 16:12:31 +08:00 ![]() @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 日志分析火葬场。 |
![]() | 79 no1xsyzy 2020-08-05 18:38:56 +08:00 @est #78 本来指令就随便添加,过分绑定到固定四个指令有点先辈的罪或者思维定势。 我觉得 POST .../login 没什么问题,我的某个工具里面 Login 是类名,将 Login 视为名词形式。 同时我觉得 POST .../order/cancellation 也没什么问题,是订单状态改变。DELETE order 和它是根本上不同的两种行为。如同 rm 一样,DELETE 谓词的使用应当慎之又慎。 一般这类框架会有自己的日志的,不用 nginx 分析日志。而且如果不分 /api/* 的 URL 出来的话,也就是 /static/* 让 nginx 处理,其他都归框架管了。而且看到某 PHP 应用的官方部署教程是关掉 /static/* 的日志的…… 基本上 nginx 日志存在有意义的信息就已经是系统层面的大问题了(比如 uwsgi 挂了) |
![]() | 80 putaozhenhaochi 2020-08-11 20:28:03 +08:00 老哥这么拼 |
![]() | 81 iplayio2019 2020-08-12 00:54:23 +08:00 @est /user/login 这种可以抽象成 session 资源,restful 很强调“资源”概念,POST /api/sessions,登录就是创建 session 。 注销登录 DELETE /api/sessions/me 取消订单本身就是状态更新,PATCH /api/orders/{orderID} <status>:<取消状态的值> |
![]() | 82 est 2020-08-12 10:08:43 +08:00 |
![]() | 83 lovedebug 2020-08-12 10:15:28 +08:00 @est RESTful 规范描述的是资源,对于非资源的情形一般需要自定义 action,这一方面大厂已经做了详细的设计,落实到具体设计就根据各自情况做了 比如你的描述提到的 1, 一般写成 POST /users/${userId}/login?type=sso 或者 login?user=xxx & type=xxx 2,一般会写成 POST /orders/${orderId}/$spilit 或者 POST /orders/${orderId}/$merge {ids:[]} 3 一般写成 POST /items/$batchUpdate {ids:[]} |
![]() | 84 est 2020-08-12 10:34:00 +08:00 @lovedebug 其实你 2 和 3 已经是另外一种风格的 URL 设计了。。。还不如干脆一条路走到黑全按照这种风格来设计 1. POST /user/login 2. POST /order/split POST /order/merge 3. POST /item/batchUpdate 多干净统一。 RESTful 就是被 UNIX 那种「所有东西都是文件」思想毒害的。遇到完全不像文件或者资源的东西,瞎搞。 |
![]() | 85 lovedebug 2020-08-12 10:46:29 +08:00 ![]() @est 对于自定义 action,RESTful 本来就没有统一,自定义 API 风格各个团队根据自己需要定义就可以 两种方案 1. 将资源 uuid 描述在 URL 中 2. 将资源 uuid 描述在 body 中 我们两人上面的就是这两种方案的体验,没有好和坏,只看对于 API 使用者的可读性。 微软和谷歌,github 对于自定义 action 也是分别有自己的实现 |
![]() | 86 no1xsyzy 2020-08-12 13:34:32 +08:00 ![]() @est #84 问题不在于 “一切皆文件”,而在于纯远端操作。 文件是对于 “可读可写” 的抽象。举上述你提到的例子: 1. 登录是一个状态,SSO 是一个多服务端共享的状态,可读可写,而且读写经过客户端传递,与文件这一抽象完美契合不成问题。 sub_site 302 到 //sso_host/user/login?to=sub_site/user/login 然后由 sso_host 确定后 302 传递 token //sub_site/user/login?with_token=~~token~~ 类比: $ cat sso/token | authorize sub_site 2. 拆分合并的核心在于它是个纯远端操作。一般来说在 Unix 下拆分文件,不出意外是 head|tail 或者 awk/sed 之类,或者对特定的文件类型也是专门的提取器而不是单独的拆分装置。然而无法保证拆分的准确性。那么显然,正确的操作应当是写一个专门的脚本完成这件事 类比过来,就是新谓词。 SPLITORDER /orders/<orderId>,请求体发送拆分准则之类的,返回 200 内容是拆分结果。 类比: $ split_order "site/orders/${orderId}" [--options ...] site/orders/a site/orders/b site/orders/c 3. 批量操作可以借用 glob,也可以是单独谓词。后者不必说,前者比如: PATCH /orders/{a,b,c} Content-Type: application/json {"coupon": "foobar"} |
![]() | 87 est 2020-08-12 14:58:13 +08:00 > SPLITORDER /orders/<orderId>,请求体发送拆分准则之类的,返回 200 内容是拆分结果。 对对对。。就是喜欢 RESTful 原教旨主义者这种一本正经发明 1000 个新词的想法。。。。 反正我对 http 的 verb 就认同 2 个,读是 GET, 写就是 POST 。你们觉得 1000 个新词最血统纯正我也没办法。。。。 |
![]() | 89 ChristopherWu 2020-08-12 17:25:26 +08:00 |
90 brickxu 2020-08-12 18:44:05 +08:00 你这涉猎够广的,另外一个贴子里还在分析 NoSQL,这边直接换到 API 设计了,然后全是 kala 搜索的域名。 |
![]() | 91 no1xsyzy 2020-08-12 19:27:24 +08:00 ![]() @est #87 奇妙,我根本不是原教旨主义者 我跟你讲,原教旨主义者的看法是,拆分订单就是删除旧订单,然后建两个新的。 至于批处理,原教旨主义者认为就应该发 100 个请求,服务器被撑爆就应该扩容、增技术 balabala 。 建新谓词是传播主义者的一支,把 RESTful 当 RPC 用。 |
![]() | 92 est 2020-08-13 10:29:43 +08:00 > 我跟你讲,原教旨主义者的看法是,拆分订单就是删除旧订单,然后建两个新的。 > 至于批处理,原教旨主义者认为就应该发 100 个请求,服务器被撑爆就应该扩容、增技术 balabala 。 > 建新谓词是传播主义者的一支,把 RESTful 当 RPC 用。 我石化了。。。。 |
93 AlbertChen 2020-09-18 09:38:45 +08:00 @gnozix 返回 JSON, 里边包含下载链接啊 |
94 goodboy95 2020-09-18 10:05:46 +08:00 反正我从来不设计 RESTapi,我只设计 api,只是长的有那么点像 REST |
![]() | 95 fhsan 2020-09-18 10:40:05 +08:00 怎么来说呢,内部自己的小项目都是 rest,对外的一律 post,因为你不知道对方有啥骚操作 |
96 kokodayo 2020-09-18 11:55:52 +08:00 @lolizeppelin REST 是,RESTful 就还好啦,毕竟部分思想还是好用的,而且 ful 到什么程度也是开发者自己说了算→_→ |
97 icew4y 2020-09-18 16:44:53 +08:00 restful 就是一残废 |
![]() | 98 abersheeran 2021-03-09 12:22:47 +08:00 @fhsan 哈哈哈哈哈哈哈哈哈,对。我写的一个 RPC 框架,全都是 POST,具体内容全部走 Body 。大家都很满意(指服务端开发和客户端开发)。 |