客戶端后端研發的必修課

前言

本文從APP后端研發同學的角度,帶來一些我們的思考和總結。

接口規范

客戶端和服務端通常采用Http(s) 協議,JSON(P)數據格式交互。通常定義的JSON格式如下:

{
    "code": 0, //接口狀態值
    "randomcode": "1515121404158",//隨機狀態碼
    "md5": null, //用來判斷數據是否更新
    "codeMsg": "成功", // 提示語,Toast使用
    "data": { //業務數據
    }
}

我們對JSON數據格式的基本要求如下:

  1. JSON數據格式要保持良好的結構,符合標準的JSON規范
    • key必須為字符串, 使用雙引號而不是單引號 , 字段命名盡量做到見名知意,命名規則建議采用駝峰命名
    • 當Value值為null時,也需要返回對應的Key ,制定協議時包含哪些字段,都需要返回
  2. data 值的數據類型為Object,不允許修改。
{
    "data": {"list":[]} //good
    "data": []   // bad
    "data": "123123"  // bad
}
  1. status需要表示狀態值時,字段從1+開始,0是一種未賦值的默認狀態,1:進行中,2:待支付,3:已完成,4:已取消
  2. 客戶端盡量只負責展示邏輯,不處理業務邏輯。
  3. 接口返回字段除了約定的必要字段,不允許返回多余的字段。
    反例:不定義Model,直接把底層的數據庫表對應的Bean返回給前端,導致多傳輸很多不必要的字段。
  4. 不允許拿變量命名json的key值
//good
[{"name":"書籍1","chapters":500},{"name":""書籍2","chapters":180},{...},...]
//bad
[{"書籍1":500},{"書籍2":180},{...},...]
  1. 禁止程序拼接json字符串,防止包含特殊轉義字符,導致客戶端解析json失敗

說明:此處沒有統一iOS、Android、Server的JSON解析框架

通用設計

APP的后端系統有些設計是通用的,各APP可以相互借鑒。

  1. 通用參數
    對于一些客戶端每次請求都需要的參數信息,我們暫且稱之為通用參數,譬如:os、appid、appversion、channel、imei 等。對于這些信息客戶端統一放在Header中。

  2. 接口簽名
    接口簽名的目的是保證服務端收到的請求都來自客戶端,一定程度增加接口被抓取的難度。
    我們采用的方法是使用攔截器處理,客戶端和服務端可以約定一些通用參數、時間戳等,前后端分別計算md5值,然后對比。
    示例代碼如下:

@Override
public ActionResult preExecute(BeatContext beat) {
  HttpServletRequest request = beat.getRequest();

  String signVal = request.getHeader(HEADER_SIGN);
  if (StringUtils.isBlank(signVal)) {
    log.info("hsign is not exist.");
    RespJsonResult jsonResult = RespJsonResult.creatFailEntity(ResponseCodeConsts.AUTH_ERR_HEADER_NOT_EXIST, "guess error lowest");
    return new JsonViewResult(JsonHelper.toJSONString(jsonResult));
  }
  StringBuffer headerBuffer = new StringBuffer(100);
  for (String headerKey : headerKeylist) {
    String headerValue = request.getHeader(headerKey);
    if (headerValue != null && headerValue.trim().length() > 0) {
      headerBuffer.append(headerValue).append("_");
    } else {
      headerBuffer.append("null_");
    }
  }
  String source = headerBuffer.append("你的秘鑰").toString();
  String localSign = MD5.encode(source);
  if (!signVal.equals(localSign)) {
    log.info("sign is illegal.source = " + source + ", localSign = " + localSign + ", hsign = " + signVal);
    RespJsonResult jsonResult = RespJsonResult.creatFailEntity(ResponseCodeConsts.AUTH_ERR_HEADER_NOT_PERMITTED, "guess error lower");
    return new JsonViewResult(JsonHelper.toJSONString(jsonResult));
  }
  return null;
}
  1. 配置中心
    動態化是一個APP的標配,小到一個icon的圖標,大到一個頁面模塊的布局。特別是針對客戶端這種 C/S 結構 ,各種開關、三方入口、動態鏈接等都需要從配置中心獲取,不寫死是我們的基本原則。
    另外,動態配置,從產品的角度,也可以靈活調整運營策略,達到快速試錯的目的。
    整體架構見:


    配置中心.png
  1. 跨域問題
    跨域問題是由于javascript語言安全限制中的同源策略造成的,簡單來說,同源策略是指一段腳本只能讀取來自同一來源的窗口和文檔的屬性,這里的同一來源指的是主機名、協議和端口號的組合。
    解決跨域問題通常采用JSONP和Access-Control-Allow-Origin。
  • JSONP
    JSONP的原理就是利用<script>標簽沒有跨域限制,來達到與第三方通訊的目的。
    示例代碼如下:
String callback = RequestUtils.getStringPara(beat, "callback", null);
if (callback != null){
    return new JsonpActionResult(callback + "("+text+")");
} else {
    return new JsonActionResult(text);
}
  • Access-Control-Allow-Origin
    Access-Control-Allow-Origin是HTML5中定義的一種解決資源跨域的策略,他是通過服務器端返回帶有Access-Control-Allow-Origin標識的Response header,用來解決資源的跨域權限問題。
    示例代碼如下:
beat.getResponse().addHeader("Access-Control-Allow-Origin","www.daojia.com");
  1. 節省流量
    對于某些不經常變動的數據,特別是配置數據,譬如:城市列表數據等,我們可以將接口返回的數據緩存在客戶端。服務端對比接口返回數據的md5值是否變化,如果無變化,不返回數據,客戶端直接使用本地緩存數據。這樣可以減少接口網絡數據量的傳輸,節省用戶流量。
    整體流程見:


    數據接口緩存.png
  2. 多版本支持
    APP后端的API比前后端分離系統的API生命周期更長,一般我們的客戶端在線上至少保留3-4個版本,還不包含我們針對一些特殊渠道定制的安裝包。這樣就要求我們后端接口在設計更加通用。
    我們通常的做法:
    對于一些修改較小的接口通過app版本號判斷做數據兼容
    對于一些修改較大的接口,通過新增接口 api/v56/order/list 的方式解決
    對于業務邏輯部分,通過app版本號獲取對應的實現類

  3. 接口管理
    與APP交互的后端web站點通常有多個,對此我們盡量做到統一收口。好處在于統一權限認證、接口簽名、參數校驗……


    服務端統一收口.png

8.接口文檔管理
接口文檔管理包括接口的描述,接口入參出參字段說明,通常這一部分文檔的形成在需求詳設階段。
這一塊我們做的不好,目前也沒有找到比較好的方案。

問題排查技巧

問題排查最困難的就是保留現場。

在APP測試過程中,我們怎么去判斷出現問題歸屬前端還是后端?

后端RD:“ iOS沒問題,android的有問題,那android的你們去查吧!” 這樣對嗎?
iOS/ Android RD:“ 我本地打斷點,看你們返回的數據有問題呀 ”
QA:“這個bug到底提給后端RD,還是提給客戶端RD呢? 容我喝杯茶想想? 抓個包看看吧! Fiddler 、Charles 走起……”

其實:客戶端和服務端通信現在唯一依賴的就是服務端返回的JSON數據,保留住現場,就能立馬確認問題歸屬。

怎么保留現場,系統實時記錄接口返回值,通過接口返回的 result 來判斷,如果result 符合,那問題歸屬端上,否則 服務端RD 乖乖去排查問題。
日志格式示例如下:

17:47:51,570  INFO jsonresult:45[51] - uid=123456789 , mobile=0 , uri = /api/guest/address/isopencity,result = {"data":{"centerLng":"39.9930060000","isOpenCity":"0","centerCityId":"1","centerCityName":"北京","centerLat":"116.3367130000","centerPoiName":"五道口"},"code":0,"codeMsg":"成功","randomcode":"1516268871570","md5":null}
18:12:25,883  INFO jsonresult:45[48] - uid=-1 , mobile=0, uri = /api/signalbomb/system/last/,result = {"data":{"lastSystem":{"id":953170805129486336,"title":"附近-服務提醒","pushTime":1516088603000,"content":"您與敏煥大號的訂單-姐姐的小店,將于01月16日 15:32開始服務,記得準時服務哦~","url":"https://m-dop71.djtest.cn/user/order/detail?role=merchant&orderid=1122089194802467776"},"newCounts":"13"},"code":0,"codeMsg":"成功","randomcode":"1516270345883","md5":null}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,836評論 6 540
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,275評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,904評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,633評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,368評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,736評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,740評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,919評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,481評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,235評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,427評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,968評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,656評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,055評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,348評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,160評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,380評論 2 379

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,818評論 18 139
  • 前言 兵馬未動,糧草先行。在一款APP產品的各個版本迭代中,兵馬的啟動指的是真正開始敲代碼的時候,糧草先行則是指前...
    listen2code閱讀 18,047評論 51 220
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,712評論 25 708
  • 計算一個整數的階乘 如果用字母n來代表一個整數,階乘代表著所有小于或等于n的整數的乘積。 階乘通常簡寫成 n! 例...
    蠟筆小狗閱讀 406評論 0 0
  • 好幾年都沒好好與你說說話了。 畢業的時候叫你寫畢業留言你還給我把紙弄丟了,畢業后給你寫信你也從來沒有回。每次一大群...
    紀南生閱讀 139評論 0 0