优秀的 REST API 设计指南

KalaSearch

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

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

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

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

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

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

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

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

KalaSearch:其它值得参考的文章:

https://stackoverflow.blog/2020/03/02/best-practices-for-rest-api-design/

https://hackernoon.com/restful-api-designing-guidelines-the-best-practices-60e1d954e7c9

https://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api

abbycin:我八股文写得特别好

baiyi:我认为在设计过程中,需要考虑 HTTP 方法的幂等性。比如 Github 的 Star 操作,为什么是 PUT 而不是 POST,就是从幂等性方面考虑的

KallyDev:补充一个微软在 GitHub 公开的规范,非常详细

https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md

shijianit:如果要接口全部加密,get 方式请求,不是会暴露出来 id 数据吗?

xuanbg:修改密码和重置密码怎么设计?软删和硬删同时存在怎么办?

xuanbg:@shijianit 所以 id 不要用自增

bsg1992:这种只适合对外的 api 并且功能单一
一个 ERP 查询几十个字段你用 get?

gnozix:想问问下载接口应该怎么设计

gowk:@bsg1992 人家说的是在卡拉搜索的业务背景下,设计优秀 API 的最佳实践,你非要拿 ERP 来杠,有意思么

nockyQ:关于版本划分这一块,除了常见的 URI 版本控制之外还有其他两种。
https://restfulapi.net/versioning/
感觉楼主可以在这个基础上展开聊一聊。

lolizeppelin:paypal api 和沙箱比微信支付漂亮太多了

但是不妨碍 paypal 垃圾微信支付好用......

KalaSearch:@nockyQ 啊是的,stripe 用的是这种。感谢你的新信息

KalaSearch:@baiyi 感谢 <3

KalaSearch:@KallyDev 这个很赞,我之前也看过,谢谢提出来,我会加到文章里

KalaSearch:@shijianit id 应该默认认为已经暴露,藏不住。楼下说的用 uuid 是个好办法,不过不管怎么样不应该认为 id 可以隐藏起来达到安全的目的。(安全我懂得不多,更详细等楼下们讨论啦)

KalaSearch:@gnozix 能说说具体场景吗?文件下载?

wellsc:restfool (逃

MrTreasure:还是缺乏具体场景,文中的内容就是是属于 restful 的标准。但是对于难点没有很好的讲解,比如 restful 如何返回错误。区分 HTTP 错误以及业务错误

ZacksT:你的 REST API 满足公司 /研发团队标准就是好的设计。接口标准可以帮助开发者规避(公司研发 /团队研发)遇见过的问题或可能遇到的问题,也可以让组内代码标准化,统一化。

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

我个人认为,只要满足团队要求的 API,就是好的 API 。具体实现各有好处,看团队取舍了

KalaSearch:@ZacksT 感谢回复。是的,满足团队、客户需求就是好 API 。对于你说的参数定义的例子,GET + URL 参数挺好的,遵从 REST 语义

@wellsc 不要淘气

bsg1992:@gowk 我并没有杠,你说回复也印证了我上面说的 [适合对外的 api 并且功能单一] r

ibreaker:小伙子很活跃啊,天天都能刷到你

lovedebug:@ZacksT 分页 GET 查询一般只带 limit 和 page,少量支持投影和过滤,如果有带其他复杂的 query 条件,其实更应该走 POST search 自定义方法

ieiayaobb:Get by Id 的比较明确,如果是 Get by name 这种,name 是唯一的怎么设计比较好?不想用 query 是因为不想在 name 不存在的时候返回空数组,而是希望也能和 Get by id 一样返回 404

xjchenhao:修改密码和重置密码,逼死强迫症?

xjchenhao:@xuanbg 修改密码和重置密码,逼死强迫症?

grzhan:之前负责撰写公司的 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 是用冒号作为前缀的,但一些路由框架会占用冒号作为关键字,因此考虑使用下划线代替。

gnozix:@KalaSearch 对前台展示的表格数据,以 excel 的格式进行下载;所以需要下载的比较多。感觉 REST 风格不太容易表示需要下载的资源

wshcdr:值得看一下

Heanes:同意 8 楼,系统内部交互可能还是“常规”的设计形式

lovedebug:@grzhan 同负责撰写 API 规范,其实关于 list 操作的 filter 功能,在实际 API 设计中有些疑惑使用场景,因为大部分情况下使用一般的 query parameter 就可以解决。我的理解是一般的 query parameter 默认是 and 操作,缺乏 or 操作以及 range value 等功能,而 $filter 主要在 url 中描述若干参数复杂的逻辑运算,如果这么做用 POST 自定义动作不是更好吗?想听一下你的理解。

xuanbg:@lovedebug 要是支持复杂的 or 和 and 条件组合,url 参数就丑的要死了……不信的可以看 kibana

szthanatos:批量操作的实践为什么很少有人谈←_←

solee:经过几年的实践,我们最后全部统一了用 POST,之前看过一篇亚马逊写的关于 Restful API 设计的改进,加入动词的描述,感觉更合理

lovedebug:@szthanatos 微软规范有谈的

lovedebug:@xuanbg 哈哈 跟业务场景有关,如果不想用万能 POST,可能只能在 url query 中支持一些 or 查询,客户在使用我们的 public api 时提出的

lovedebug:@solee 自定义动词应该在已有的 RESTful 规范不满足时候才使用

Nolink:收藏了,谢谢分享

ericls:用现成的 query language 不好吗? 非要把 http headers 滥用成 query 还要自己定义 实现 维护……

Amit:@xuanbg
密码一般都是要做 hash 的,且不能暴露给前端,所以需要对这个字段单独修改,而不能放到完整信息中修改并返回,修改密码是在登录状态下,所以我会设计为 PUT /v1/users/{id}/password (管理员修改用户密码)或 PUT /v1/users/self/password (修改自己的密码),重置密码我理解为非登录状态下修改密码(不确定用户身份),所以我会设计为 PUT /v1/users/password,然后再 body 中提供用户名、验证码等信息。

软删除也是删除,对应用来说如果删除了就是不存在的,应用中不应该能看到,软删除和物理删除同时存在是不合理的,这种情况应该设计一个状态字段区分,而不是使用逻辑删除。

xuanbg:@Amit
修改密码和重置密码我也是一样的处理。在复数形式的资源后面,有时候不但要加动词,还得加属性,以定位到更细一层的资源才行。

我说的软删其实是禁用,只是为了理解方便。业务前端看不到了,也就没得用了。但元数据管理后端应该能看到,毕竟禁用后说不得还会启用。。。硬删当然就是数据灰灰,再也无法恢复的。如果软删用 PUT:/v1/users,那就和修改姓名冲突了。我是这样规划的,修改普通属性 PUT:/v1/users,禁用 PUT:/v1/users/status,删除 DELETE:PUT:/v1/users 。

jorneyr:RESTful 在 URL 里是禁止使用动词的,但是很多时候有的 URL 中用动词来表达很自然,强制使用 RESTful 的风格的话会很难受

imhxc:我一直有个问题,请教下。
在实际业务中,各种需求都有,很难严格遵守 RESTful API,拿文章中的示例来说:

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

这种怎么设计?

codingbody:我有个问题问大家,为什么安全扫描的时候,不准我使用除了 GET 、POST 之外的请求,我认为请求的方式和安全没啥关系吧

DeWhite:那个就一句话,吃屎啦。就是没有主语的,国内的很明显主语省略的句子还有很多。

xcstream:这标题隐含意思就是不 rest 就不优秀(狗头)

KalaSearch:@imhxc 用 ACL 来控制,REST endpoint 没办法控制的

KalaSearch:@DeWhite 你说的是祈使句,祈使句当然可以没有主语(省略了第二人称主语)

forgaoqiang:看了下 Discuz Q,真的几斤,完全的 RESTFUL 风格,patch delete 各种方法都用

grzhan:@lovedebug 我个人觉得关于复杂查询不管是用 $filter 还是直接 POST 自定义方法(如 "_search" )都是可以的,具体看自己场景。
事实上我们项目实际实践中,这种情况还是自定义 POST 方法用的比较多

GavinZZ:??

GavinZZ:还有个叫车满满的。。。工资给开的还算可以 13K+ 14 薪,但是不推荐去,企业文化很奇葩

lovedebug:@grzhan 嗯。$filter 需要写 parser 专门处理,否则会重复造轮子

grzhan:@lovedebug 如果查询场景需求确实很复杂的业务的话,我们会考虑上 GraphQL 的

lovedebug:@grzhan 主要是 GraphQL 对已有产品的 RESTful API 破坏性过大,ROI 也不够高,另外也考虑在微服务和 k8s 中 GraphQL 中心化并不是一个很完美的方案。其实主要的阻力是项目进度和同事。哈哈哈哈

dongxiaoxian:好复杂

ChanKc:@codingbody 没有,但是历史上发生过一些 HTTP server 对 PUT,DELETE 等请求实现不当,导致远程代码执行等漏洞。一些公司就会觉得索性禁了这些请求更好

yixinlove:@KallyDev 好东西

wangxiaoaer:这个帖子很有启发啊,顺便问一下,针对楼上一些老哥们提到的复杂的组合条件查询,如果是基于 spring boot + jpa 的应用,如何优雅的实现呢?

cbasil:设计 API 的目的是为了前端好评? api 接口安全和效率都不需要考虑了吗?你去看看阿里,腾讯等大公司的接口文档,有几个是完全按照 REST API 来设计的。

lovedebug:@cbasil 一是对内为了公司内部统一,减少沟通成本。而是针对 public api 与主流统一,减少用户的集成成本。

nig001:不错的

fy:@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

sunzhenyucn:请让我默默地 mark 一下

lovedebug:@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:看到 API 和表对应,我就知道不用看下去了,太水了。

jy28520:@KalaSearch 想问下我们现在的业务需要验证用户提交的 SKU 和优惠券是否匹配 请问 URL 应该怎么设计那?
我们会有几条 SKU 和几条优惠券的信息

b0644170fc:根本不需要 rest, get / post 走天下

imhxc:@fy 嗯嗯,ACL 是可以解决刚才提的问题。
但是总感觉 REST API 规范有局限性,自己曾经做过 ERP,会经常出现较为复杂的接口,感觉很难严格遵守 REST API 风格。

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

我以前自己写接口用 REST API 写着写着就要精神分裂了。。。?

也可能是我没理解 REST API 的精髓?

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

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

imhxc:@no1xsyzy 感谢,涨知识了。

lolizeppelin:这个论坛早就有人说过了

RESTful 是对 sql 的劣质模仿,没法表达的情况多去了

no1xsyzy:@lolizeppelin #73 谁?在哪儿说的?
RESTful sql 劣质模仿 site:v2ex.com 只搜出来你说的话……

从来从来,RESTful 就是个和 SQL 完全相悖的路线
SQL 一直在做得越来越图灵完备,添加各种诡异的、我承认确实像是有那么回事儿的、但其实没有也没关系的功能进去。
RESTful 一直都是那么平铺直叙。谓宾仍然是谓宾,最多用点 HTTP 语义。
“C 是个对 Lisp 的劣质模仿”

lolizeppelin:@no1xsyzy
est 说的 嘿嘿

no1xsyzy:@lolizeppelin #75 @est 在哪说的?
楞是没搜到……

lolizeppelin:@no1xsyzy
当然个别字有出入呗,你找他 233333

est:@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:@est #78 本来指令就随便添加,过分绑定到固定四个指令有点先辈的罪或者思维定势。
我觉得 POST .../login 没什么问题,我的某个工具里面 Login 是类名,将 Login 视为名词形式。
同时我觉得 POST .../order/cancellation 也没什么问题,是订单状态改变。DELETE order 和它是根本上不同的两种行为。如同 rm 一样,DELETE 谓词的使用应当慎之又慎。
一般这类框架会有自己的日志的,不用 nginx 分析日志。而且如果不分 /api/* 的 URL 出来的话,也就是 /static/* 让 nginx 处理,其他都归框架管了。而且看到某 PHP 应用的官方部署教程是关掉 /static/* 的日志的…… 基本上 nginx 日志存在有意义的信息就已经是系统层面的大问题了(比如 uwsgi 挂了)

putaozhenhaochi:老哥这么拼

iplayio2019:@est /user/login 这种可以抽象成 session 资源,restful 很强调“资源”概念,POST /api/sessions,登录就是创建 session 。
注销登录 DELETE /api/sessions/me

取消订单本身就是状态更新,PATCH /api/orders/{orderID}
<status>:<取消状态的值>

est:@iplayio2019

那么问题来了

1. 一次登入多个站点的 SSO 怎么设计 URL
2. 订单拆分、合并操作如何表达?
3. 上面的同学提到的,批量操作如何写{orderID} ?

lovedebug:@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:[]}

est:@lovedebug 其实你 2 和 3 已经是另外一种风格的 URL 设计了。。。还不如干脆一条路走到黑全按照这种风格来设计

1. POST /user/login
2. POST /order/split POST /order/merge
3. POST /item/batchUpdate

多干净统一。

RESTful 就是被 UNIX 那种「所有东西都是文件」思想毒害的。遇到完全不像文件或者资源的东西,瞎搞。

lovedebug:@est 对于自定义 action,RESTful 本来就没有统一,自定义 API 风格各个团队根据自己需要定义就可以
两种方案
1. 将资源 uuid 描述在 URL 中
2. 将资源 uuid 描述在 body 中
我们两人上面的就是这两种方案的体验,没有好和坏,只看对于 API 使用者的可读性。
微软和谷歌,github 对于自定义 action 也是分别有自己的实现

no1xsyzy:@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"}

est:> SPLITORDER /orders/<orderId>,请求体发送拆分准则之类的,返回 200 内容是拆分结果。

对对对。。就是喜欢 RESTful 原教旨主义者这种一本正经发明 1000 个新词的想法。。。。

反正我对 http 的 verb 就认同 2 个,读是 GET, 写就是 POST 。你们觉得 1000 个新词最血统纯正我也没办法。。。。

est:@lovedebug 我也差不多是这个观点。RESTful 其实没必要完全照搬。取其精华,去其糟粕。团队内部统一认识就行。

ChristopherWu:@est
@no1xsyzy
@lovedebug

RESTFul 是不是还有一个问题就是,强绑定于 HTTP ?
假如我的业务除了 HTTP 接口给外部团队使用外,还要提供二进制协议的接口给内部团队使用,那么 Restful 就不通用了。
相对来说,使用 RPC 风格的协议,可以统一 API,不管什么,code,message,data 三个字段封装起来就是业务层,其他 code 是协议层的事情。

brickxu:你这涉猎够广的,另外一个贴子里还在分析 NoSQL,这边直接换到 API 设计了,然后全是 kala 搜索的域名。

no1xsyzy:@est #87 奇妙,我根本不是原教旨主义者
我跟你讲,原教旨主义者的看法是,拆分订单就是删除旧订单,然后建两个新的。
至于批处理,原教旨主义者认为就应该发 100 个请求,服务器被撑爆就应该扩容、增技术 balabala 。

建新谓词是传播主义者的一支,把 RESTful 当 RPC 用。

est:> 我跟你讲,原教旨主义者的看法是,拆分订单就是删除旧订单,然后建两个新的。
> 至于批处理,原教旨主义者认为就应该发 100 个请求,服务器被撑爆就应该扩容、增技术 balabala 。
> 建新谓词是传播主义者的一支,把 RESTful 当 RPC 用。

我石化了。。。。

Celery REST API - python

有没有办法将Celery用于以下用途:使用Form参数将对外部U​​RL的HTTP调用排队(HTTP Post to网址)外部URL将响应HTTP响应,200、404、400等,如果响应采用错误非200 ish响应的形式,将重试重试一定次数,并将根据需要退出使用REST API将Task / Job / Work队列添加到Celery,将URL传递给call…

api-mom 一个 API 在线管理工具

andychen1:免费,安全,简单易用界面简洁。支持团队多人协作,只需安装 chrome 浏览器 api-mom 扩展就可以对 API 进行测试。 项目里的文件夹,接口列表,接口 Tab 都支持鼠标拖动排序。 编辑接口时支持常用快捷键 Ctrl + S 保存。 接口文档和测试结果并排,一切都在眼前,方便校对。 接口越来越多,可以用模糊搜索快速找出。 浏览模…

REST API Base64映像imagecreatefromstring():数据不是可识别的格式 - php

我正在为Android应用开发REST API。我想将base64图像另存为从应用程序发送的jpeg图像。我在laravel干预下为此编写了代码,并与邮递员进行了测试。没问题。但是,当数据从Android App发送时,他们说我在收到错误Image intervention - Image source not readable时出现了500条错误。之后,我…

各种有知道有哪些稳定的 API 数据云服务厂商吗?

inktiger:想在本地使用一个简单 html 做一个小东西,不想搭载服务,想到用 api 数据云,在云端设计数据库表,使用 api 用 ajax 调用进行增删改查就行,目前我知道的有 apicloud 支持这么玩,可我感觉他不靠谱,还有什么其他比较做的好的吗

请问 根据 REST api 生成 spring 客户端代码有什么办法?

chenhui7373:输入: https://{api-host}/v1/player/immediateControl/power 请求参数 { "playerIds":[ "4PBXun3mQoZGnKdLKoDtBA==", "4PBXun32QoZGnKdLKoDtBA==" ], "option":1 } 响应参数 { "success":[ "…