PageSourceSearch

http://ibeetl.com/assets/index.html-eErKCDWs.js

js ibeetl.com collected 2026-09-25 20:05:46 UTC 22,560 bytes, 35 lines download raw bytes

1import{_ as a,c as n,b as s,a as e,o as i}from"./app-B8MQJI6s.js";const l={};function d(r,t){return i(),n("div",null,t[0]||(t[0]=[s(`<p>如果没时间看,记住3点</p><ul><li><p>REST以资源为中心设计的API,数据架构和数据模型设计对REST接口质量有很大影响</p></li><li><p>遇事不决就用POST</p></li><li><p>参考业界的REST API,如github,elasticsearch</p></li></ul><h2 id="rest-背景" tabindex="-1"><a class="header-anchor" href="#rest-背景"><span>REST 背景</span></a></h2><p>2000年,互联网兴起数年,企业系统广泛流行通过HTTP 实现远程调用,代替EJB,当时基于HTTP远程调用,无论URL的设计,参数命名,以及响应都没有统一的规范。Roy Fielding 针对此问题, 提出了表示状态转移(Representational State Transfer),简称REST,作为一种设计web服务的体系结构方法 .<a href="http://www.ruanyifeng.com/blog/2011/09/restful.html" target="_blank" rel="noopener noreferrer">REST</a> 是目前最流行的HTTP API 设计规范,用于 Web 接口的设计 。</p><p>REST的核心思想就是,客户端发出的指令都是&quot;标准动词 + 操作资源&quot;的结构。比如,<code>GET /orders</code>/1这个命令,<code>GET</code>是动词,<code>/orders</code>/1 是宾语,这里的1是指订单编号为1的订单。</p><p>如下是一个REST请求</p><div class="language-http line-numbers-mode" data-ext="http" data-title="http"><button class="copy" title="Copy code" data-copied="Copied"></button><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code><span class="line"><span>GET https://xxxx/orders/1 HTTP/1.1</span></span>
2<span class="line"><span>Accept: application/json</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div></div></div><p>响应</p><div class="language-http line-numbers-mode" data-ext="http" data-title="http"><button class="copy" title="Copy code" data-copied="Copied"></button><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code><span class="line"><span>HTTP/1.1 200 OK</span></span>
3<span class="line"><span></span></span>
4<span class="line"><span>{</span></span>
5<span class="line"><span>&quot;code&quot;:&quot;SUCCESS&quot;</span></span>
6<span class="line"><span>&quot;messsage&quot;:&quot;成功&quot;</span></span>
7<span class="line"><span>&quot;data&quot;:{&quot;orderId&quot;:1,&quot;orderValue&quot;:99.90,&quot;productId&quot;:1,&quot;quantity&quot;:1}</span></span>
8<span class="line"><span>}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>这里的标准动词是HTTP规范提供的标准,如上面的get,其他动作还有post,delete,将会在后面描述。</p><p>相比于HTTP 远程调用,REST是以资源作中心构建的远程调用服务,这里的资源指的是业务实体,业务实体来自于数据架构。一个良好的REST API设计,离不开良好的数据架构</p><p>以GIT 提供的服务为例子,资源包括了</p><ul><li>仓库</li><li>用户</li><li>Issues</li><li>Release</li><li>Team</li></ul><p>以物联网为例子,资源包括了</p><ul><li>设备</li><li>用户</li><li>家庭</li><li>属性</li><li>告警</li><li>OTA</li><li>命令</li></ul><h2 id="rest和其他协议对比" tabindex="-1"><a class="header-anchor" href="#rest和其他协议对比"><span>REST和其他协议对比</span></a></h2><p>在上一节,比较过二进制协议和HTTP的区别,建立在HTTP之上的REST,具备HTTP的优点</p><ul><li>语言独立: 所有语言都支持HTTP协议,也提供REST风格的客户端接口</li><li>平台独立: 所有的系统都支持HTTP</li><li>可观测性强: 通过HTTP代理服务器,以及良好的REST接口定义,可以让系统之间调用可观测,
8可统计</li></ul><p>基于HTTP协议的除了REST外,还有非常流行的SOAP,简单区分俩种的区别</p><table><thead><tr><th></th><th>REST</th><th>SOAP</th></tr></thead><tbody><tr><td>核心</td><td>资源</td><td>服务(方法),像传统分布式调用Corba,EJB的的HTTP版本</td></tr><tr><td>量级</td><td>轻便,可选约束</td><td>重量级,必须约束</td></tr><tr><td>适用范围</td><td>任何系统,服务之间;终端和系统之间</td><td>系统之间,如人力系统和财务系统</td></tr></tbody></table><h2 id="rest最佳实践" tabindex="-1"><a class="header-anchor" href="#rest最佳实践"><span>REST最佳实践</span></a></h2><p>本节介绍REST的设计最佳实践,它除了上一节的分布式API通用的最佳实践外,REST API 还有 特定的设计原则,总结为下表内容和表后的重点说明</p><table><thead><tr><th>设计原则</th><th>描述</th></tr></thead><tbody><tr><td>URL</td><td>URL必须定义了资源,比如/books/1978</td></tr><tr><td>命名规范</td><td>驼峰命名方法,蛇形命名法,烤肉串命名法。比如对于显示名称 display name。分别对应了displayName,display_name, dispaly-name。 REST的URL通常使用烤肉串命名法,入参和出参数使用Java常用的驼峰命名法</td></tr><tr><td>资源名称</td><td>API的核心是操作是WEB资源或者业务实体,因此需要在这之前定义好这些名称,这些名称在数据架构或者更早的的数据架构前已经定好。比如用户中心提供的REST接口查询用户的绑定设备GET /users/devices 。如果缺少标准名称,另外一个接口查询设备状态 GET /devs/customer . 这让API使用者有困惑,他可能认为devices和devs 可能是物联网的俩种设备资源名称通常以复数形式体现</td></tr><tr><td>资源层级</td><td>当资源必须通过层级获取的时候,可以用/表达层级,比如/orders/{orderId}/items, 建议层级不超过3层。</td></tr><tr><td>标准字段</td><td>定义常见的标准字段,如requestId,token,createTime,orderBy,displayName,startDate,endDate等. 其他行业特定的字段应该基于数据架构的概念模型来定义,比如物联网中的device,shadow, 电商中的coupon,order,sku</td></tr><tr><td>开闭区间</td><td>表示范围,需要统一规定是否包含临界值,Java通常使用[、),起始值闭区间,结束值为开区间,推荐使用这种约定</td></tr><tr><td>使用单位后缀</td><td>比如优先使用maxByte代替max,除非有上下文指示返回的单位。 关于时间,精确到秒,应该使用time,精确到天,应该使用date后缀。</td></tr><tr><td>名字缩写</td><td>软件开发者常用的缩写可以用在API上,如id,config,电商中的Sku。</td></tr><tr><td>属性</td><td>class是java的关键字,因此在设计API的时候需要避免参数中使用此关键字,这主要考虑到序列化和反序列化时候,这类关键字无法被实现; is开头的属性有可能在反序列化中失败: isCreate,有的序列化成isCreate,有的序列化成create ;需要明确属性不存在和属性值为null是同一个含义 明确空集合和null是同一个含义 ;客户端是否支持浮点数,以JS为例子,<a href="https://blog.csdn.net/u010028869/article/details/86563382" target="_blank" rel="noopener noreferrer">不支持Java返回的过长的数字</a></td></tr><tr><td>版本号</td><td>通常使用version表示api的版本,比如/gms/v1.0/devices,建议REST URL 总是包含version。另外可选的是在HTTP Header中包含version。比如github api推荐把version放到header里</td></tr></tbody></table><p>下表列出了 <a href="https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#list-repositories-for-the-authenticated-user" target="_blank" rel="noopener noreferrer">Github REST API</a></p>`,24),e("table",null,[e("thead",null,[e("tr",null,[e("th",null,"REST接口"),e("th",null,"说明")])]),e("tbody",null,[e("tr",null,[e("td",null,"/user/repos"),e("td",null,"获取当前用户的所有仓库")]),e("tr",null,[e("td",{repo:""},"/repos/{owner}/"),e("td",null,"查询一个仓库信息")]),e("tr",null,[e("td",null,"/repos/{owner}/{repo}/releases"),e("td",null,"来自github api,返回代码库的所有release")]),e("tr",null,[e("td",{release_id:""},"/repos/{owner}/{repo}/releases/"),e("td",null,"来自github api,返回代码库的指定的release 信息")])])],-1),s(`<p>下列情况是错误的URL例子</p><table><thead><tr><th>
8REST接口</th><th>正确</th><th>错误分析</th></tr></thead><tbody><tr><td>/book</td><td>/books</td><td>通常使用复数,除非查询单个对象</td></tr><tr><td>/user/create</td><td>/user</td><td>URL 不应该包含动词</td></tr><tr><td>/users/{id}/books.json</td><td>/users/{id}/books</td><td>无需指定返回的格式,通常默认是json,如果需要其他格式,可以通过媒体类型来指定,比如<strong>application/xml</strong></td></tr><tr><td></td><td></td><td></td></tr></tbody></table><p>思考:查看你系统的的REST API,看看有没有不符合RESTAPI 设计原则的</p><p><strong>标准方法命名</strong></p><p>HTTP提供了标准的动作 GET,PUT,POST,DELETE等,</p><ul><li>GET:读取</li><li>POST:新建</li><li>PUT:创建或者更新</li><li>PATCH:更新(Update),通常是部分更新</li><li>DELETE:删除(Delete)</li></ul><table><thead><tr><th>URL</th><th>动作</th><th>含义</th></tr></thead><tbody><tr><td>/users</td><td>GET</td><td>查询所有用户</td></tr><tr><td>/users/1</td><td>GET</td><td>查询id为1的用户</td></tr><tr><td>/users</td><td>POST</td><td>创建新的用户</td></tr><tr><td>/users/1</td><td>PUT</td><td>创建或者更新用户,id为1</td></tr><tr><td>/users/1</td><td>DELETE</td><td>删除用户1</td></tr><tr><td>/users</td><td>DELETE</td><td>删除所有用户</td></tr></tbody></table><p><strong>范围查询</strong></p><p>规定使用offset和limt作为查询参数,如果数据来源是大数据,通常使用cusor方式查询,翻页范围使用cusor和limit参数俩个,这里的cusor通常是递增主键id</p><p><strong>HTTP</strong> <strong>Stauts</strong></p><p>在开发API时候,对于REST的响应结果,还需要进一步规范如下API说明,通常用如下</p><ul><li>200 表示成功</li><li>400 表示客户端错误 <ul><li>400 服务器不理解请求,如参数错误</li><li>401 未通过身份验证</li><li>403 未授权的访问资源</li><li>404 访问资源不存在</li></ul></li><li>500 表示服务端错误</li></ul><p>关于HTTP Status 定义,参考<a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status" target="_blank" rel="noopener noreferrer">HTTP 响应状态</a></p><p>如果这些Status Code不足以表达更明确的含义,建议在响应体的JSON里包含code 进一步明确错误含义,比如code为PARAMETER_ERROR 表示参数校验错误。message通常用于补充code含义</p><div class="language-http line-numbers-mode" data-ext="http" data-title="http"><button class="copy" title="Copy code" data-copied="Copied"></button><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code><span class="line"><span>HTTP/1.1 200 OK</span></span>
9<span class="line"><span>Content-Type: application/json</span></span>
10<span class="line"><span></span></span>
11<span class="line"><span>{</span></span>
12<span class="line"><span>&quot;code&quot;:&quot;SUCCESS&quot;</span></span>
13<span class="line"><span>&quot;messsage&quot;:&quot;库存查询成功&quot;</span></span>
14<span class="line"><span>&quot;data&quot;:{</span></span>
15<span class="line"><span>   </span></span>
16<span class="line"><span>}</span></span>
17<span class="line"><span>}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>错误响应: 对于基于HTTP协议的API,可以充分利用HTTP STATUS来表示API是否成功,STATUS 304 表示授权错,STATUS 500 表示内部失败,等等,使用HTTP STATUS也有利于其他系统,如网关或者监控系统对错误进行观测。</p><div class="language-http line-numbers-mode" data-ext="http" data-title="http"><button class="copy" title="Copy code" data-copied="Copied"></button><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code><span class="line"><span>HTTP/1.1 500 ?</span></span>
18<span class="line"><span>Content-Type: application/json</span></span>
19<span class="line"><span></span></span>
20<span class="line"><span>{</span></span>
21<span class="line"><span>&quot;code&quot;:&quot;PARAMETER_ERROR&quot;</span></span>
22<span class="line"><span>&quot;messsage&quot;:&quot;操作失败,&quot;</span></span>
23<span class="line"><span>&quot;data&quot;:{</span></span>
24<span class="line"><span>   </span></span>
25<span class="line"><span>}</span></span>
26<span class="line"><span>}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>使用HTTP Status,还是使用响应的Body中的code来指示REST的响应结果,屈居于俩个因素</p><p>1) HTTP Status仍然能粗略的表达REST 响应状态,屈居于客户端是否对状态更详细的要求,比如HTTP 500表示服务器内不错误ï¼
26Œå¯¹äºŽå¤§å¤šæ•°ç”µå•†å’Œä¼ä¸šåº”用系统,是不足以表达的</p><p>1) 可观测性,如果已经搭建好的有可观测系统,且REST返回结果能被客观性系统收集到,则可以使用CODE来指示结果。 如果此时搭建的有NGINX 的日志监控,HTTP Status也能粗略的统计到REST 请求结果</p><p>如果API实现异步调用,需要通过文档向调用者说明,从哪里可以获取异步执行结果。或者提供一个新的API,用户可以通过reqeust_id,查询异步调用结果。或者服务端返回一个查询ID,用户可以用此ID查询结果</p><p>或者对于REST请求,也可以在响应中包含一个URL,供用户查询。如下一个成功响应</p><div class="language-http line-numbers-mode" data-ext="http" data-title="http"><button class="copy" title="Copy code" data-copied="Copied"></button><pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code><span class="line"><span>HTTP/1.1 200 OK</span></span>
27<span class="line"><span>Content-Type: application/json</span></span>
28<span class="line"><span></span></span>
29<span class="line"><span>{</span></span>
30<span class="line"><span>&quot;code&quot;:&quot;ASYNC-SUCCESS&quot;</span></span>
31<span class="line"><span>&quot;messsage&quot;:&quot;成功&quot;</span></span>
32<span class="line"><span>&quot;data&quot;:{</span></span>
33<span class="line"><span>    &quot;url&quot;:&quot;/device/ECD0017R1/status/344fer55656563&quot;</span></span>
34<span class="line"><span>}</span></span>
35<span class="line"><span>}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>无论是成功或者失败,都应该包含code和message,code是成功或者错误的CODE定义,message是用户可阅读的的消息。</p><p>对于响应的JSON,还有如下设计建议</p><ul><li>需要明确的区分其属性是否为null,或者不存在</li><li>返回的空集合,使用[],而不是null,或者不存在</li><li>返回的浮点数或者长整形,如果客户端是JS,需要注意到有可能JS不支持,这情况下改成字符串</li></ul><h2 id="总有例外" tabindex="-1"><a class="header-anchor" href="#总有例外"><span>总有例外</span></a></h2><p>本节包含了通常REST API 最佳实践,有其他资料对API 最佳实践有更严格的规定,架构师需要衡量是否采用这些严格规定, 本文列举了几个需要衡量的点</p><ul><li>充分使用HTTP Status Code,有些REST规范要求使用更多的HTTP Status Code表示,比如对于客户端错误,采用403表示权限不足,,404表示访问资源不存在使用。参考rfc9110 了解更多status含义。本书建议使用200,400,以及500即可,更详细的错误信息放在code里</li><li>REST API的GET查询不推荐携带Body。允许GET with Body,即允许GET查询携带Body。这是因为可能查询参数结构较为复杂,比如ES提供的/_search, 其body就是一个JSON格式。再比如,通常客户端JS框架会把页面查询条件的表单序列化成JSON。因此通常建议使用POST请求,且URL路径上增加查询动作,比如/books/search</li><li>删除通常是用DELETE,如果是按照条件删除,可以使用POST,比如ES中的按照查询条件删除 POST /my-index-000001/_delete_by_query</li><li>REST API 大部分实践都要求参数是蛇行命名。本书建议,蛇行命名,以可以像Java那样驼峰命名</li></ul><p>通常,基于GET请求的REST,意味着 通过URL分享此REST接口,其结果易于被客户端或者代理缓存,支持重定向等功能,如果GET支持BODY,那必须确认HTTP 代理或者是WEB 框架是否支持。</p><h2 id="rest服务成熟度模型" tabindex="-1"><a class="header-anchor" href="#rest服务成熟度模型"><span>REST服务成熟度模型</span></a></h2><p>个人觉得比较扯淡,也不建议企业按照REST成熟度模型构建服务</p><h2 id="引用" tabindex="-1"><a class="header-anchor" href="#引用"><span>引用</span></a></h2><p>如何定义REST接口,还可以参考业内的一些实践,比如Github REST API,ElasticSerch REST API</p><ul><li><p>原始文章: https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm</p></li><li><p>Github REST 实践: https://docs.github.com/en/rest</p></li><li><p>GitHub例子: <a href="https://github.com/github/docs/blob/main/content/rest/using-the-rest-api/getting-started-with-the-rest-api.md" target="_blank" rel="noopener noreferrer">Github API </a></p></li><li><p>Elastic Search 例子 https://www.elastic.co/guide/en/elasticsearch/reference/current/rest-apis.html</p></li><li><p>推荐:<a href="http://ruanyifeng.com/blog/2018/10/restful-api-best-practices.html" target="_blank" rel="noopener noreferrer">阮一峰:RESTful API 最佳实践</a></p></li><li><p>推荐:<strong><a href="https://opensource.zalando.com/restful-api-guidelines/index.html#_zalando_restful_api_and_event_guidelines" target="_blank" rel="noopener noreferrer">Zalando RESTful API and Event Guidelines</a></strong></p></li><li><p>
35推荐 微软 <a href="https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design" target="_blank" rel="noopener noreferrer">RESTful web API design </a></p></li><li><p>博客 <a href="https://blog.stoplight.io/api-design-patterns-for-rest-web-services" target="_blank" rel="noopener noreferrer">API Design Patterns for REST</a></p></li><li><p>https://blog.stoplight.io/api-design-patterns-for-rest-web-services)</p></li></ul>`,35)]))}const o=a(l,[["render",d],["__file","index.html.vue"]]),c=JSON.parse('{"path":"/article/rest-api-design/","title":"分布式API设计 下:REST","lang":"en-US","frontmatter":{"title":"分布式API设计 下:REST","tags":["架构","新书"],"createTime":"2025/02/18 23:06:15","permalink":"/article/rest-api-design/","description":"如果没时间看,记住3点 REST以资源为中心设计的API,数据架构和数据模型设计对REST接口质量有很大影响 遇事不决就用POST 参考业界的REST API,如github,elasticsearch REST 背景 2000年,互联网兴起数年,企业系统广泛流行通过HTTP 实现远程调用,代替EJB,当时基于HTTP远程调用,无论URL的设计,参数命...","head":[["meta",{"property":"og:url","content":"http://ibeetl.com/article/rest-api-design/"}],["meta",{"property":"og:site_name","content":"闲大赋"}],["meta",{"property":"og:title","content":"分布式API设计 下:REST"}],["meta",{"property":"og:description","content":"如果没时间看,记住3点 REST以资源为中心设计的API,数据架构和数据模型设计对REST接口质量有很大影响 遇事不决就用POST 参考业界的REST API,如github,elasticsearch REST 背景 2000年,互联网兴起数年,企业系统广泛流行通过HTTP 实现远程调用,代替EJB,当时基于HTTP远程调用,无论URL的设计,参数命..."}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"en-US"}],["meta",{"property":"og:updated_time","content":"2026-05-20T04:25:34.000Z"}],["meta",{"property":"article:tag","content":"架构"}],["meta",{"property":"article:tag","content":"新书"}],["meta",{"property":"article:modified_time","content":"2026-05-20T04:25:34.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"分布式API设计 下:REST\\",\\"image\\":[\\"\\"],\\"dateModified\\":\\"2026-05-20T04:25:34.000Z\\",\\"author\\":[]}"]]},"headers":[],"readingTime":{"minutes":10.2,"words":3059},"git":{"updatedTime":1779251134000,"contributors":[{"name":"lijiazhi","username":"lijiazhi","email":"[email protected]","commits":3,"avatar":"https://gravatar.com/avatar/1b5114831459724ad4acc394423b4b48ecfa4a7e0659fbe07a0ce5181a1050a7?d=retro"}]},"autoDesc":true,"filePathRelative":"article/架构/distribute-api-rest-design.md","categoryList":[{"id":"92a2b5","sort":10001,"name":"article"},{"id":"9a9aed","sort":10005,"name":"架构"}]}');export{o as comp,c as data};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.