API的設(shè)計(jì)(2) - protoapi協(xié)議

上一節(jié)講API的錯(cuò)誤處理時(shí),是java定義為例胰伍,講述了正常結(jié)果盟戏、異常結(jié)果常見(jiàn)異常庆寺、錯(cuò)誤四種層次的劃分,我把使用這種返回劃分的風(fēng)格的API叫做:protoapi诉字。

protoapi適用于同一語(yǔ)言懦尝、同一進(jìn)程內(nèi)的API接口定義;同時(shí)也會(huì)適用于跨語(yǔ)言壤圃、跨進(jìn)程的API接口陵霉,比方說(shuō)常見(jiàn)的http + json的API。

在單一語(yǔ)言?xún)?nèi)伍绳,我們?cè)谠O(shè)計(jì)類(lèi)庫(kù)踊挠,提供接口的時(shí)候,會(huì)使用語(yǔ)言提供的接口 interface對(duì)API做嚴(yán)格的定義冲杀。

我們使用類(lèi)庫(kù)的時(shí)候效床,看其接口,便可以對(duì)其提供的API有準(zhǔn)確的認(rèn)知权谁,有多少個(gè)API剩檀,每個(gè)API需要的參數(shù)是什么,返回的結(jié)果是什么旺芽,異常是什么等等沪猴,都可以準(zhǔn)確的知道;我們調(diào)用的時(shí)候采章,也會(huì)有編譯器確保我們的調(diào)用API的方式是正確的运嗜。

當(dāng)對(duì)于http + json的API時(shí),API文檔往往是以網(wǎng)頁(yè)甚至word共缕、excel的文件來(lái)提供文本描述洗出。

這是糟糕的做法:

  • 協(xié)議不明確
    • API究竟有哪些?結(jié)果的屬性有哪些图谷?什么類(lèi)型翩活?文本描述是非常容易含糊其事的阱洪,特別是對(duì)于那些直接粘貼返回結(jié)果json結(jié)構(gòu)的文檔
  • 難以版本化管理
    • API是經(jīng)常需要修改的菠镇,如何確保當(dāng)前看到的excel文件里面記錄的是最新的API冗荸?
  • 調(diào)用不便
    • 每個(gè)調(diào)用者都需要去自己開(kāi)發(fā)一套“SDK”,重復(fù)定義文檔中的結(jié)構(gòu)是屬于重復(fù)勞動(dòng)

IDL

科學(xué)的做法利耍,應(yīng)該是引入一個(gè)IDL - Interface Definition Language對(duì)API做嚴(yán)格的定義蚌本。

thriftprotobuf/gRPC等都是優(yōu)秀隘梨、成熟的IDL程癌。

我曾經(jīng)使用過(guò)thrift多年,在thrift的基礎(chǔ)上做了各種二次開(kāi)發(fā)轴猎,但這兩年則逐漸切換為protobuf上嵌莉,主要因?yàn)椋?/p>

  • protobuf語(yǔ)法提供了一定的擴(kuò)展能力
  • 編譯器protoc有很好的插件機(jī)制,二次開(kāi)發(fā)更加便利

還是以登陸為例捻脖,我們使用grpc作為為IDL的話锐峭,會(huì)是:

syntax = "proto3";

import "protoapi.proto";

package account_api;

message CommonError {
  enum ErrorTypes {
    UNKNOWN = 0;
    INVALID_TOKEN = 1;
    INVALID_PARAMETER = 2;
  }
}

message LoginReq {
  string username = 1 [(required) = true, (format) = "email"];
  string password = 2 [(required) = true];
}

message User {
  string username = 1;
  int user_level = 2;
  string nick = 3;
  bool has_avatar = 3;
}

message AccountBalance {
  uint64 amount_available = 1;
  uint64 amount_due = 2;
}

message LoginResp {
  User user = 1;
  AccountBalance = 2;
}

message LoginError {
  enum ErrorTypes {
    UNKNOWN = 0;
    INVALID_LOGIN = 1;
    ACCOUNT_BANNED = 2;
  }

  ErrorTypes error = 1;
}

service Account {
  option (CommonError) = FOO;

  rpc login (LoginReq) returns (LoginError) {
    option (BizError) = "LoginError";
  }
}

HTTP + JSON

使用protobuf作為IDL,不意味著我們一定需要使用它默認(rèn)綁定的http2以及二進(jìn)制的序列化可婶。

我們使用IDL沿癞,首先是要解決文本描述的文檔的各種問(wèn)題;然后矛渴,我們可以通過(guò)自行實(shí)現(xiàn)protobuf的插件椎扬,來(lái)實(shí)現(xiàn)包括服務(wù)器端API提供方,不同語(yǔ)言調(diào)用方SDK的代碼生成曙旭。

而既然代碼是我們自行生成出來(lái)盗舰,我們當(dāng)然可以去實(shí)現(xiàn)使用 http + json晶府。

具體到HTTP桂躏,protoapi推薦使用以下風(fēng)格的實(shí)現(xiàn):

URL

每個(gè)服務(wù)對(duì)應(yīng)一個(gè)API endpoint,比方說(shuō):gateway.mydomian.TLD/api

URL endpoint添加PATH川陆,區(qū)分service以及method剂习,比方說(shuō):

  • gateway.mydomian.TLD/api/Account/login

HTTP Method

  • 所有API默認(rèn)使用HTTP POST
  • 特效場(chǎng)景下可以使用GET,但不鼓勵(lì)
    • 因?yàn)閝uery string無(wú)法很好的對(duì)復(fù)雜請(qǐng)求對(duì)象做序列化

返回結(jié)果

使用以下不同的HTTP statuscode來(lái)區(qū)分不同的返回結(jié)果:

  • 正常結(jié)果 Response:200
  • 業(yè)務(wù)異常 BizError:400
  • 框架異常 CommonError:420
  • 錯(cuò)誤 Error:HTTP 500
    • 當(dāng)HTTP返回超時(shí)较沪,或者遇到其它的序列化鳞绕、傳輸問(wèn)題時(shí),也都應(yīng)該認(rèn)為是遇到錯(cuò)誤Error

最后

protoapi所鼓勵(lì)的尸曼,是劃分結(jié)果们何、異常、錯(cuò)誤等API風(fēng)格控轿;即便不使用任何IDL冤竹,代碼生成拂封,只要一套API對(duì)其返回結(jié)果做了劃分,我們也可以說(shuō)它是符合protoapi協(xié)議的鹦蠕。

后續(xù)我會(huì)在講protoapi的代碼生成實(shí)現(xiàn)(會(huì)開(kāi)源冒签!),以及討論protoapirestful钟病、swagger等的關(guān)系萧恕。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市肠阱,隨后出現(xiàn)的幾起案子票唆,更是在濱河造成了極大的恐慌,老刑警劉巖屹徘,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惰说,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡缘回,警方通過(guò)查閱死者的電腦和手機(jī)吆视,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)酥宴,“玉大人啦吧,你說(shuō)我怎么就攤上這事∽竟眩” “怎么了授滓?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)肆糕。 經(jīng)常有香客問(wèn)我般堆,道長(zhǎng),這世上最難降的妖魔是什么诚啃? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任淮摔,我火速辦了婚禮,結(jié)果婚禮上始赎,老公的妹妹穿的比我還像新娘和橙。我一直安慰自己,他們只是感情好造垛,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布魔招。 她就那樣靜靜地躺著,像睡著了一般五辽。 火紅的嫁衣襯著肌膚如雪办斑。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天杆逗,我揣著相機(jī)與錄音乡翅,去河邊找鬼吁讨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛峦朗,可吹牛的內(nèi)容都是我干的建丧。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼波势,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼翎朱!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起尺铣,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤拴曲,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后凛忿,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體澈灼,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年店溢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叁熔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡床牧,死狀恐怖荣回,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情戈咳,我是刑警寧澤心软,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站著蛙,受9級(jí)特大地震影響删铃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜踏堡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一猎唁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧暂吉,春花似錦胖秒、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)挤渔。三九已至肮街,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間判导,已是汗流浹背嫉父。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工沛硅, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人绕辖。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓摇肌,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親仪际。 傳聞我的和親對(duì)象是個(gè)殘疾皇子围小,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容