<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          淺析 Open API 設(shè)計規(guī)范

          共 7311字,需瀏覽 15分鐘

           ·

          2022-06-27 16:36

          背景

          最近由于業(yè)務(wù)需求,我參與研發(fā)的云產(chǎn)品 CSB 需要對外開放 Open API,原本不是什么難事,因為阿里云內(nèi)部的 Open API 開放機(jī)制已經(jīng)非常成熟了,根本不需要我去設(shè)計,但這次的需求主要是針對一些獨立部署的場景,需要自行設(shè)計一套規(guī)范,那就意味著,需要對 Open API 進(jìn)行一些規(guī)范約束了,遂有此文。

          Open API 和前端頁面一樣,一直都是產(chǎn)品的門面, Open API 不規(guī)范,會拉低產(chǎn)品的專業(yè)性。在云場景下,很多用戶會選擇自建門戶,對接云產(chǎn)品的  Open API,這對我們提出的訴求便是構(gòu)建一套成熟的 Open API 機(jī)制。

          站在業(yè)務(wù)角度,有一些指導(dǎo)原則,指導(dǎo)我們完善 Open API 機(jī)制:

          • 前端頁面使用的接口和 Open API 提供的接口是同一套接口
          • 任意的前端頁面接口都應(yīng)該有對應(yīng)的 Open API

          站在技術(shù)角度,有很多的 API 開放標(biāo)準(zhǔn)可供我們參考,一些開源產(chǎn)品的 Open API 文檔也都非常完善。一方面,我會取其精華,另一方面,要考慮自身產(chǎn)品輸出形態(tài)的特殊性。本文將圍繞諸多因素,嘗試探討出一份合適的 Open API 開放規(guī)范。

          Open API 設(shè)計考慮因素

          一個完善的 Open API 規(guī)范到底應(yīng)該規(guī)范哪些東西?

          站在設(shè)計角度,需要考慮:命名規(guī)范,構(gòu)成規(guī)范,路徑規(guī)范,出入?yún)⒁?guī)范,數(shù)據(jù)類型規(guī)范,統(tǒng)一返回值規(guī)范,錯誤碼規(guī)范,分頁規(guī)范。

          站在團(tuán)隊角度,團(tuán)隊中的后端初級中級開發(fā)以及前端研發(fā)是否有足夠的經(jīng)驗,領(lǐng)悟并落地好制定的 API 規(guī)范。同時,伴隨著人員流動,這份 Open API 規(guī)范是否可以很好地被傳承下去。

          站在行業(yè)角度,需要考慮提供 Open API 的產(chǎn)品所在的市場是否已經(jīng)成熟,API 風(fēng)格可能已經(jīng)有了對應(yīng)的規(guī)范。

          站在產(chǎn)品角度,每個產(chǎn)品適合的 API 風(fēng)格是不同的,下文會著重探討這一角度。

          總之,Open API 的設(shè)計是很難形成定論的一個東西,我在介紹自身產(chǎn)品最終采用的 Open API 規(guī)范之前,會先來聊一下大家耳熟能詳?shù)囊恍└拍?,例?restful。

          restful 規(guī)范之爭

          有人的地方就會有江湖。

          有代碼的地方也是如此。

          如果你在碼圈混,一定聽說過 restful 規(guī)范:

          • 增刪改查應(yīng)分別聲明為:POST、DELETE、PUT、PATCH、GET

          • 不應(yīng)該出現(xiàn)動詞,動詞統(tǒng)一由 HTTP Method 表示

          • 體現(xiàn)出“資源”的抽象

          • 利用 pathVariable,queryParam,header,statusCode 表達(dá)很多業(yè)務(wù)語義

          restful 規(guī)范看似美好,但如果你真正嘗試過落地,一定會遇到一些類似的問題:

          • 以用戶登錄接口為例,此類接口難以映射到資源的增刪改查
          • 以查詢最近 7 個小時內(nèi)的接口請求錯誤率為例,衍生到諸如 graphQL 這類復(fù)雜的查詢場景,往往需要 json 結(jié)構(gòu),GET 是無法實現(xiàn)這一點的,只有 POST 才可以傳遞

          基于此,restful 規(guī)范逐漸有了反對的聲音:

          • 強(qiáng)行讓所有的事物都“資源”化一下,有悖于開發(fā)常識,接口不一定都能夠通過簡單的增刪改查來映射
          • 復(fù)雜的查詢語義不一定能夠用 GET 表達(dá)

          restful 風(fēng)格的擁躉者,不乏對這些反對言論進(jìn)行抨擊,社區(qū)中不免有“拒絕 restful 風(fēng)格的主要是低水平不思進(jìn)取的架構(gòu)師和前后端程序員們,不會設(shè)計是人的問題,不是規(guī)范的問題”此類的言論。同時對 restful 進(jìn)行了升華:復(fù)雜參數(shù)的檢索問題,在 restful 語義中本就應(yīng)當(dāng)歸類為 post,因為該行為并不是對資源的定位(GET),而是對資源的檢索(POST)

          這顯然刺激了 restful 風(fēng)格反對者的神經(jīng),不屑道:呵,愚蠢的 restful 原教旨主義者呀。

          不知道你是 restful 的擁躉者還是反對者?亦或是,中立者。

          restful 之爭暫時到此為止,這番爭論純屬虛構(gòu),看官不必計較。無論你如何看待 restful,下面我的論述,你都可以作為一個中立者,否則效果減半。

          ROA 與 RPC

          API 設(shè)計并不只有 restful 一種規(guī)范,在更大的視角中,主流的 API 設(shè)計風(fēng)格其實可以分為

          • 面向資源的設(shè)計,即 ROA(Resource oriented architecture)
          • 面向過程的設(shè)計,即 RPC(Remote Procedure Call)

          restful 便是 ROA 風(fēng)格的典型例子,而 RPC 風(fēng)格則相對而言不太容易被大家熟知,但實際上可能大多數(shù)的系統(tǒng)的接口是 RPC 風(fēng)格的,只不過 RPC 風(fēng)格這個概念不太為人所知。

          以用戶模塊的 CRUD 為例,對比下兩個風(fēng)格:

          ROA 風(fēng)格

          創(chuàng)建用戶(POST)

          Request:
          POST /users

          {"name""kirito""age": 18}

          Response:
          HTTP 201 Created

          {"id": 1, "name""kirito""age": 18}

          查詢用戶(GET)

          Request:
          GET /users/1

          Response:
          HTTP 200 OK

          {"id": 1, "name""kirito""age": 18}

          查詢用戶列表(GET)

          Request:
          GET /users

          Response:
          HTTP 200 OK

          {[{"id": 1, "name""kirito""age": 18}], "next""/users?offset=1"}

          創(chuàng)建/修改用戶(PUT)

          Request:
          PUT /users/1

          {"name""kirito""age": 19}

          Response:
          HTTP 200 OK

          {"id": 1, "name""kirito""age": 19}

          修改用戶(PATCH)

          Request:
          PATCH /users/1

          {"age": 20}

          Response:
          HTTP 200 OK

          {"id": 1, "name""kirito""age": 20}

          刪除用戶(DELETE)

          Request:
          DELETE /users/1

          Response:
          HTTP 204 No Content

          ROA 風(fēng)格和 restful 規(guī)范說明的是一回事,為方便其與 RPC 風(fēng)格接口的對比,特此說明上面示例的一些值得關(guān)注的點:

          • 使用 HTTP 響應(yīng)碼(200,201,204),完成 HTTP 語義與業(yè)務(wù)語義的映射,異常流也出現(xiàn) 404,401 等情況(出于篇幅考慮,本文未做異常流的介紹)
          • PATCH 部分修改資源,請求體是修改部分的內(nèi)容;PUT 創(chuàng)建/修改資源,請求體是新資源全部的內(nèi)容
          • id 是資源定位符,而 age、name 則為屬性

          RPC 風(fēng)格

          創(chuàng)建用戶(POST)

          Request:
          POST /user/createUser

          {"name""kirito""age": 18}

          Response:
          HTTP 200 OK

          {"code": 0, "message""""data": {"id": 1, "name""kirito""age": 18}}

          查詢用戶(POST)

          Request:
          POST /user/getUser

          {"id": 1}

          Response:
          HTTP 200 OK

          {"code": 0, "message""""data": {"id": 1, "name""kirito""age": 18}}

          查詢用戶列表(POST)

          Request:
          POST /user/listUsers

          Response:
          HTTP 200 OK

          {"code": 0, "message""""data": {"user": [{"id": 1, "name""kirito""age": 18}], "next""/user/listUsers?offset=1"}}

          修改用戶(POST)

          Request:
          POST /user/modifyUser

          {"id": 1, "name""kirito""age": 19}

          Response:
          HTTP 200 OK

          {"code": 0, "message""""data": {"id": 1, "name""kirito""age": 19}}

          修改用戶名稱(POST)

          Request:
          POST /user/modifyUserAge

          {"id": 1, "age": 20}

          Response:
          HTTP 200 OK

          {"code": 0, "message""""data": {"id": 1, "name""kirito""age": 20}}

          刪除用戶(DELETE)

          Request:
          POST /user/deleteUser

          {"id": 1}

          Response:
          {"code": 0, "message"""}

          RPC 風(fēng)格不像 restful 一類的 ROA 風(fēng)格存在一些約定俗成的規(guī)范,每個業(yè)務(wù)系統(tǒng)在落地時,都存在差異,故此處只是筆者個人的經(jīng)驗之談,但愿讀者能夠求同存異:

          • user 為模塊名,不需要像 ROA 風(fēng)格使用復(fù)數(shù)形式
          • 使用明確的動賓結(jié)構(gòu),而不是將 CRUD 映射到 HTTP Method,HTTP Method 統(tǒng)一使用 POST,查詢場景也可以使用 GET
          • 返回值中攜帶 code、message 和 data,來映射響應(yīng)狀態(tài)及響應(yīng)信息,一般可以自行定義 code 的狀態(tài)碼,本文使用 0 標(biāo)識請求成功,message 僅在業(yè)務(wù)響應(yīng)失敗時有意義,data 代表業(yè)務(wù)響應(yīng)結(jié)果

          如何選擇 RPC 和 ROA,則需要根據(jù)產(chǎn)品自身的業(yè)務(wù)情況進(jìn)行決策。有如下的指導(dǎo)原則:

          • 有復(fù)雜業(yè)務(wù)邏輯的 API ,無法使用簡單的增、刪、改、查描述時宜使用 RPC 風(fēng)格。
          • 如果業(yè)務(wù)所屬行業(yè)標(biāo)準(zhǔn)要求 restful 風(fēng)格 API 或 ROA 能夠滿足業(yè)務(wù)需求,宜使用 ROA 風(fēng)格。

          AWS 主要采用 RPC 風(fēng)格,Azure、Google 主要采用 ROA(restful)風(fēng)格,阿里云 OpenAPI 同時支持 RPC 和 ROA,以 RPC 為主。

          盡管規(guī)范是無罪的,但在 ROA 風(fēng)格在實踐過程中,我還是見識過不少“坑”的:

          • 要求資源先行,即先設(shè)計資源,后設(shè)計接口,對軟件開發(fā)流程要求較高
          • 錯誤的 ROA 設(shè)計案例 1:tomcat 等應(yīng)用服務(wù)器在處理 DELETE 方法的 HTTP 請求時,默認(rèn)不允許攜帶 request body,需要顯式開啟,導(dǎo)致刪除失敗。(此案例為設(shè)計者的問題,復(fù)雜的刪除場景,不應(yīng)當(dāng)映射成 DELELE,而應(yīng)改成 POST,DELETE 不應(yīng)當(dāng)攜帶 request body)
          • 錯誤的 ROA 設(shè)計案例 2:restful 路徑中攜帶的參數(shù),可能會引發(fā)正則匹配的問題,例如誤將郵箱作為路徑參數(shù),或者多級路徑匹配的沖突問題(此案例為設(shè)計者的問題,復(fù)雜的查詢場景,不應(yīng)當(dāng)映射成 GET,而應(yīng)改成 POST,path 中只應(yīng)該出現(xiàn)資源定位符,而不應(yīng)當(dāng)攜帶屬性)
          • 響應(yīng)碼為 404 時,較難區(qū)分是真的 path 不存在,還是資源不存在
          • 不利于對接網(wǎng)關(guān)等需要配置路由轉(zhuǎn)發(fā)的場景

          CSB 的 Open API 規(guī)范希望滿足以下的需求:

          • 后端開發(fā)設(shè)計接口時,有明確的設(shè)計思路,不至于因為一個接口到底用 POST 還是 GET 實現(xiàn)而糾結(jié),不用花費太多時間在資源的抽象上(這并不是說明資源是不需要被設(shè)計的)
          • 前端開發(fā)對接接口時,能夠較快地與后端協(xié)同,并且利于前端接口的封裝
          • 用戶對接 Open API 時,整體風(fēng)格一致,模塊清晰

          綜上,在設(shè)計風(fēng)格選擇上,我計劃采取 RPC 的設(shè)計規(guī)范??偨Y(jié)一下 RPC 風(fēng)格的優(yōu)勢:

          • API 設(shè)計難度較低,容易落地
          • 阿里云大多數(shù)成熟的 IAAS 層產(chǎn)品使用 RPC 規(guī)范
          • 適合復(fù)雜業(yè)務(wù)場景

          一個詳細(xì)的 RPC 接口文檔示例

          創(chuàng)建服務(wù)

          請求參數(shù)

          序號字段中文名字段英文名數(shù)據(jù)類型必填說明
          1名稱namestring顯示名稱
          2協(xié)議protocolstring枚舉值:http/grpc/webservice
          3負(fù)載均衡lbstring枚舉值:random/roundrobin
          4上游類型upstreamTypestring枚舉值:fixed/discovery
          5節(jié)點列表nodesarrayupstreamType=fixed 時必填,示例:[{"host": "1.1.1.1","port": "80","weight": "1"}]
          6來源idoriginIdstring
          7服務(wù)名稱serviceNamestring注冊中心中的名稱,upstreamType=discovery 時必填
          8服務(wù)描述descriptionstring
          9網(wǎng)關(guān)idgatewayIdstring

          返回參數(shù)

          序號字段中文名字段英文名數(shù)據(jù)類型說明
          1響應(yīng)碼codeint0 標(biāo)識成功;1 標(biāo)識失敗
          2響應(yīng)信息messagestring
          3響應(yīng)結(jié)果datastring返回服務(wù) id

          請求示例

          POST /service/createService

          Request:
          {
            "name""httpbin",
            "protocol""http",
            "lb""random",
            "upstreamType""fixed",
            "nodes": [
              {
                "host""httpbin.org",
                "port""80",
                "weight""1"
              }
            ],
            "gatewayId""gw-1qw2e3e4"
          }

          Response:
          {
            "code"0,
            "message""",
            "serviceId""s-1qw2e3e4"
          }

          API 命名規(guī)范

          • API 應(yīng)使用拼寫正確的英文,符合語法規(guī)范,包括單復(fù)數(shù)、時態(tài)和語言習(xí)慣

          • 不能出現(xiàn)多個含義相近但功能無實際差別的 API,如同時存在 /user/getUser 和 /user/describeUser

          • 語言習(xí)慣:禁止使用拼音

          • 如下常見場景的命名規(guī)則是固定的

            • 日期時間類型的參數(shù)應(yīng)命名為 XxxxTime。例如:CreateTime
          • 常用操作名稱規(guī)范

            • create:創(chuàng)建
            • modify:變更
            • delete:刪除
            • get:獲取單個資源詳情
            • list:獲取資源列表
            • establishRelation:建立資源關(guān)系
            • destroyRelation:銷毀資源關(guān)系

          總結(jié)

          以本文推崇的一條規(guī)范為例:"所有接口全部使用 POST",這不是為了遷就低水平不思進(jìn)取的架構(gòu)師和前后端程序員們(我在社區(qū)論壇上看到的言論),而是為了提高開發(fā)效率,降低溝通成本,降低運維和錯誤定位成本,把瞎折騰的成本,投入到了其他比如業(yè)務(wù)架構(gòu)設(shè)計,測試體系,線上監(jiān)控,容災(zāi)降級等領(lǐng)域上。

          接口規(guī)范也并非我總結(jié)的那樣,只有 RPC 和 ROA,也有一些言論將 GraphQL 單獨歸為一類 API 設(shè)計風(fēng)格,用于復(fù)雜查詢場景,有興趣的同學(xué)可以參考 es 的 API 文檔。

          綜上,我計劃采用 RPC 的 API 設(shè)計風(fēng)格。

          參考資料

          kong:https://docs.konghq.com/gateway/2.8.x/admin-api/

          google restful api design:https://cloud.google.com/apis/design?hl=zh-cn

          https://www.zhihu.com/question/336797348


          瀏覽 66
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  蜜桃av日韩网址 免费观看黄色毛片 | 女女综合网 | 婷婷激情丁香五月天 | 欧美性爱91 | 欧美乱伦xxxx |