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

上一節(jié)講API的錯誤處理時,是java定義為例,講述了正常結(jié)果異常結(jié)果常見異常錯誤四種層次的劃分,我把使用這種返回劃分的風格的API叫做:protoapi

protoapi適用于同一語言、同一進程內(nèi)的API接口定義;同時也會適用于跨語言、跨進程的API接口,比方說常見的http + json的API。

在單一語言內(nèi),我們在設(shè)計類庫,提供接口的時候,會使用語言提供的接口 interface對API做嚴格的定義。

我們使用類庫的時候,看其接口,便可以對其提供的API有準確的認知,有多少個API,每個API需要的參數(shù)是什么,返回的結(jié)果是什么,異常是什么等等,都可以準確的知道;我們調(diào)用的時候,也會有編譯器確保我們的調(diào)用API的方式是正確的。

當對于http + json的API時,API文檔往往是以網(wǎng)頁甚至word、excel的文件來提供文本描述

這是糟糕的做法:

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

IDL

科學的做法,應(yīng)該是引入一個IDL - Interface Definition Language對API做嚴格的定義。

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

我曾經(jīng)使用過thrift多年,在thrift的基礎(chǔ)上做了各種二次開發(fā),但這兩年則逐漸切換為protobuf上,主要因為:

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

還是以登陸為例,我們使用grpc作為為IDL的話,會是:

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,不意味著我們一定需要使用它默認綁定的http2以及二進制的序列化。

我們使用IDL,首先是要解決文本描述的文檔的各種問題;然后,我們可以通過自行實現(xiàn)protobuf的插件,來實現(xiàn)包括服務(wù)器端API提供方,不同語言調(diào)用方SDK的代碼生成。

而既然代碼是我們自行生成出來,我們當然可以去實現(xiàn)使用 http + json。

具體到HTTP,protoapi推薦使用以下風格的實現(xiàn):

URL

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

URL endpoint添加PATH,區(qū)分service以及method,比方說:

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

HTTP Method

  • 所有API默認使用HTTP POST
  • 特效場景下可以使用GET,但不鼓勵
    • 因為query string無法很好的對復雜請求對象做序列化

返回結(jié)果

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

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

最后

protoapi所鼓勵的,是劃分結(jié)果、異常、錯誤等API風格;即便不使用任何IDL,代碼生成,只要一套API對其返回結(jié)果做了劃分,我們也可以說它是符合protoapi協(xié)議的。

后續(xù)我會在講protoapi的代碼生成實現(xiàn)(會開源!),以及討論protoapirestfulswagger等的關(guān)系。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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