The Web backend

of Android mobile

TonyQ 



我是誰

  • JavaScript.tw 社群 creator    (因為很忙越來越少辦聚會了......><)
  • 現在
    •  QNAP senior web developer
    • 作這個的公司   ↓
       
    • NAS (圖為 TS -470P)
  • 以前
    • 5945.tw Architect
    • ZK senior developer

Android  Newbie

Android 開發經驗: 5 個月


Ptt  看板《AndroidDev》
6/08 TonyQ        □ [閒聊] 那些我從 android app 學到的事情


Senior Web developer

Web 開發經驗:8 年

JavaScript + Html + CSS
Web Backend(JavaEE/PHP/ASP/ASP.NET...)

議程介紹

  1. 前文
  2. API 網址設計
  3. API 資料回傳討論
  4. API 送交資料討論
  5. Android GCM 
    push notification 實作探討

Story begins

5945 呼叫師傅 : 

網站接到使用者需求,需要將訊息傳遞給師傅

簡訊?  無法精準描述、傳送使用者提供的圖片
Email ?  不夠即時,撰寫麻煩,師傅難以管理
Line ?  需要人工,系統自動化處理很麻煩



所以


我們自己來寫一個 app 吧  <( ̄︶ ̄)>

為什麼是 Android


因為我們當時自己做的調查顯示,
台灣水電跟裝潢師傅有智慧型手機的,

有六成以上用 android 。

不要問我為什麼,我也不知道。 <( ̄︶ ̄)>

開始之前


能用 SSL (HTTPS) 就用 

安全考量、降低網路 sniffer 風險,能作就做

前提條件:


  1. 使用者資料來自於自有 Server 
  2. 有些資料要一直往 client 送
     (整合網站後台)
  3. 有些資料會由 client 往 server 送。

有資料傳遞:需要身份驗證
(不然會被看光光改光光...)

問題:


身份認證

前提:因為已有師傅資料庫,所以採用客服提供給師傅認證碼的模式,讓師傅輸入認證碼通過啟動。

想法一:


(Inspired  from ptt's article .)
直接用 JDBC 連遠端伺服器的資料庫

不夠安全


client app 程式碼、連線
基本上都可能有被看光光

放 DB 帳號、密碼無疑等著被自殺

零分

想法二:


用 web server 的 session 來管理登入狀態,
透過 web 登入,用 cookie 的 session id 來保持登入狀態。

受限於 web session timeout 時間

程式碼寫起來複雜。

API 程式碼無法獨立撰寫條件判斷


還是零分

想法三:


把帳號密碼或驗證碼存起來, 
每次 call api 都帶著作身份驗證。


如果是硬幹的 third-party app  這無可厚非,如果是在設計 server side API ,那就是拿使用者開玩笑。

小心網路監聽風險

想法四:


Call 登入的 api 後,發一個 web accessToken ,
以這個 accessToken 作為存取檢查依據。

ex.
post   http://myserver.com/api/login
code="123456"

{ status:0 , result: { accessToken: "da39a3ee5e6b4b0d" } }


Access Token 


應有使用期限 (expire-date)  設計,定期自我更新(或請使用者重新驗證) 、更新後停用舊的,永遠的通行碼 = 永遠的問題

你永遠不知道使用者什麼時候會被竊聽偷走 token ,而且他可能還會繼續跟使用者一起偽裝成 client 。

立委都會被監聽了,網路當然也會被 sniffer  。(咦)

ACCESS TOKEN


不同的 device 使用不同 access token ,方便追蹤控管。

Access token 本身不要夾帶太多資料,
盡量把資料留在 server 。*

 * 有一種作法是 access_token 本身是用 AES 加密過的資料,server 取得時用該 pk 反解回原本資料,優點是解密時不用查表,但個人還是比較建議直接把資料存 server。 



想好權限怎麼管


再來要來煩惱怎麼設計資料 API


API Design is free


就跟一般 Server Side 的 controller 一樣,
根據不同需求就會有不同 api 。


Free is not good.

Begin with URL


網址通常是 web API 的靈魂,怎麼命名是一個學問


一般是以資料為主,由資料出發。

以 5945 例子,我們有 Request (需求單) 資料表、
User 資料表、Work (工單) 資料表。

Restful style

很多人講、很多人推,你直覺要想到的應該是這個
  • List
    • GET  http://myapiserver.com/api/request/
  • Read
    • GET   http://myapiserver.com/api/request/1
  • Create
    • PUT http://myapiserver.com/api/request/
  • Update 
    • PUT http://myapiserver.com/api/request/1
  • Delete
    • Delete http://myapiserver.com/api/request/1

The world is not beautiful

Restful 看起來很美好,但還是有許多細節需要注意。

Restful 

很多人不瞭解 http method 該怎麼處理
PUT / DELETE 

學會、找到適合的平台了就好,不是太大問題。

Restful

跨 model 的操作不方便

以 5945 為例,師傅進行一個「接案」的動作,
實際上同時更新案件資料表、工單、新增給客戶的資訊。

難以歸類到特定 model 的操作,
而且往往有些操作只是更新局部位。

如果要拆成多個 api call ,則資料操作不具有單元性。


Sample


POST http://myapiserver.com/api/work/1/accept
accept=1

vs

PUT http://myapiserver.com/api/work/1/
accept=1
???

Restful 


從 model 出發而不是從操作出發,
容易疏忽、漏寫權限控管


有些欄位不是所有人、所有情境下都能更新,
容易為了符合 rest 卻造成複雜的狀態判斷。

有些 readonly 的 model 不應該開 update 或 delete 操作。

Restful 

pagination 
  • 一般
    • GET http://myserver.com/api/requests
      ?limit=50&offset=100
      • limit = 數量
      • offset = 起始位置
  • 時間相關
    • GET http://myserver.com/api/posts
      ?since=timestamp&limit=100
      • since 起始時間
      • limit 數量









Restful

容易取得多餘資料

有時候其實只是想知道某個狀態,卻回來一整包資料。

Restful 

Request is expensive 

多一個 request = 多一個失敗的幾會
多一個 request = 多花一點時間

在不太鑽牛角尖的前提下,盡量合併 request 是好事。

瞭解 rest 的精神


讓大家操作資料時有共同的共識,
但不要拘泥於形式,該提供特別 api 時就作。



瞭解 API 的目的


給 user 自由操作 or 給自家工程師 call 。

facebook 有提供 FQL 、yahoo 有 YQL ,
但一般 app - web 的資料交換,是否需要?

由目的決定方法。


API 是會改版的


非常容易在一開始被忘記的事情,
出新版本 app 新功能需要修改既有 api,怎麼辦? 


可能有一堆使用者正在用舊版本 app ,
而且他們不一定想升級...

都還有使用者在用 IE6 了....


API Version issue


網址前面增加版本號 prefix 
 GET http://myapiserver.com/api/v1/request/1

個人認為相對簡單的作法

API version issue


Header 夾帶版本訊息
Accept: application/vnd.company.myapp.customer-v1+json

  • 優點是不用更換網址
  • 缺點是要在 server side 自己進行判斷、
    轉到對應處理的 controller 。


API version issue


關於兩種作法的相關討論 (非常精彩,值得一讀~)



搞定網址,還有內容


先討論 API  接收回來的內容





API 一定會遇到的狀況


Error :
  • 身份驗證失敗
  • API server 掛了
  • API 版本已不支援
  • 沒有權限的操作
    (如讀別人的資料之類的)
  • 參數錯誤
  • 各式各樣的應用層執行失敗

Error 類型是無限的


根據不同操作,會有許多不同種情境出現,
要有一致的處理策略,建議採用 Error code  處理。

  • -1 : server 掛了( db fail..etc)
  •  0  : 成功
  •  1  : 沒有操作權限
  •  2  : 版本過期
  •  3  : 身份驗證失敗
  •  4  : 參數錯誤
  • .... 其他錯誤 



Error message


server  一般會回傳錯誤訊息以提供更多 debug 資訊。

 一般建議 api 錯誤訊息以給 client app 工程師閱讀為主,丟出訊息要謹慎處理,不能讓使用者看到 server 程式碼、行數或 call stack。

萬一 db 帳號密碼隨著 error message 一起流出就...




ERROR MESSAGE


client 應該另外根據 error code 準備給 user 看的錯誤訊息
跟對應處理方式,不宜直接將 server 訊息拿來顯示,

理由是:
  1. 錯誤訊息的語系問題
  2. 同一個 api 的錯誤,
    在不同操作下可能有不同解讀方式

格式


JSON response ? 
XML response ?

app client 看得懂就好,沒有太大問題...

傳 binary 如圖片,一般是另外給網址由 client 自己下載,
不建議轉 base 64 進 json/xml string 。

格式


    status: <code> 
    message: "<error message>" , 
    data: { <result object> }
} 

一般建議至少回傳值要有這些欄位。
所有的 api 操作都該有回傳值以利 client 判斷成功失敗。

傳資料


GET 有資料長度的問題,也不應該拿 GET 來作資料更新,
GET 要盡量保持 state less 的原則。

有資料的更新、異動,使用 POST。



格式


 post Parameter 
vs
post JSON 



post Parameter

原生的瀏覽器傳遞資料方式
  • 優點
    • 支援度佳
    • 不用花時間/資源將資料轉成 json 字串
    • 支援檔案上傳的成本較小(改成 multipart 就好)
  • 缺點
    • 處理巢狀資料或陣列麻煩
      • 巢狀資料可以用 request[index] =1
        這類表達式另外處理 
      • 陣列可以用 request[]=1&request[]=2 處理

Post Json

  • 優點
    • 資料結構彈性大
  • 缺點
    • Server & Client 需要 JSON parse/serialize 
      有效能跟時間考量
    • 要支援檔案上傳只能轉 base64
      • 檔案會變大 33%
      • 很難用 streaming (in-place) 傳輸檔案
      • 假設要上傳一個 3M 的檔案,轉 base64 變 4M 字串,加上組裝成 json  後上傳,至少需要 8M 以上的空間
        對 client 記憶體小的狀況很不方便

Push notification

推播通知




why push notification


傳統資料模型
Client
Server 
Client 

使用者需要主動詢問是否有新資料(polling)

Polling issue


  1. 耗電
  2. 對 server 造成負擔
  3. 無法「即時」,受限於 polling 時間差
    (五分鐘一次..etc)

PUSH NOTIFICATION 

Server 
↓ 
Client

Server
Client

Server 發現有新資料,送通知(最多 4kb) 給 client ,
client 視狀況回 server 取得新資料做處理。

How  GCM works ? 


注意事項:


send notification to client

一個簡單的 post  request 搞定
$apiKey = "123456";  //從 google developer console 取得

// 從 client 收到的 regID ,可以一次發給多個。(一個 id 代表一個 android client) $registrationIDs = array( "123", "456" );// 要送出的訊息,最多 4k 請留意$message = "x"; $message = "my message";// Set POST variables
$url = 'https://android.googleapis.com/gcm/send';$fields = array( //要送出的資料,json 格式 'registration_ids'  => $registrationIDs, 'data'              => array( "message" => $message ), );$headers = array( 'Authorization: key=' . $apiKey, 'Content-Type: application/json' );// ... curl post code...

最後

Web Api  Controller 
VS
傳統 Web Controller


  •  stateless  API
  • no session involve 
  • 更容易被亂踹

Q&A

雖然我想應該是不會有時間問問題...XD



FOLLOW US

JavaScript.tw FB group

Slide

Thanks

南部研討會萬歲~!XD

Web Api of Android backend

By TonyQ Wang

Web Api of Android backend

  • 9,094