那些讓人皺眉的 REST API

這年頭無論是在桌面或行動環境裡頭做些 Native 的應用程式,都免不了要橋接一些 Web Service 的 API。做個生產力工具嘛,也需要把檔案儲存在雲端裡頭,甚至直接透過網路同步,做個遊戲嘛,也需要把分數上傳到 Facebook 還是 Twitter 上面,美其名叫做分享,比較貼切的說法該是炫耀。

這幾年工作下來,也陸陸續續遇到一些 API,有的是開放的,有的是文件上面就打上個「極機密」字樣。不管是哪種,其中不少,實在讓人不禁眉頭一皺。

首先,很多的 API 規劃似乎都以為 client 軟體都不會出問題,都不需要測試,於是做出讓 client 端無法做自動測試的規劃。這種問題主要發生在開放 API 上,像有些服務一方面開放 API,一方面又設定了什麼禁止灌水、禁止廣告機器人、禁止濫發沒有意義內容干擾其他使用者…這類管理規則,但,client 軟體做自動測試時,必定需要發送一定數量的測試訊息,不能灌水就等於無法測試,無法測試就等於無法開發。

這樣的問題不難解決:想要不干擾其他使用者,又可以自動測試,就是在服務中提供一塊測試專區;但現實總是開放 API 的服務總是不會想到這點,而結果是,就算 API 公開出來,也不會有什麼人能夠為這套服務寫出什麼應用。

像 OAuth 這種流行的認證機制,也一樣讓 desktop/mobile 軟體無法自動測試。OAuth 的設計是,client 軟體想要使用某套服務時,使用者的帳號密碼不需要經過 client 端的程式,在需要登入時,直接開啟外部瀏覽器、或是 client 軟體中開啟一個瀏覽器元件,使用者在網頁表單中輸入帳號密碼,登入成功之後,server 再透過一組協定好的 callback URL,將 access token 傳回 client,client 之後只要憑這個 access token,就可以取用其他各項服務。

這麼做的好處是,因為 client 端不經手使用者的帳號密碼,可以保障使用者的隱私資料不會被 client 端取得。壞處呢,一方面,從使用者的角度來看,這到流程實在很不直覺—我明明在這套應用程式按下登入,但為什麼接下來出現的,卻是這套程式被切到背景去,另外跳出了瀏覽器畫面出來?

另一方面,就是無法自動測試—以 Mac OS X/iOS 平台來說,如果中間需要跳出網頁,那麼就無法使用 SenTestingKit 做 Unit Test,而非得要寫成一個至少有基本 UI 的應用程式,才能完成登入流程,於是變成略過基本元件的測試,直接進入 UI 階段的測試,這樣就會出現測試的盲點—到底程式有沒有問題,是到了有 UI 之後才開始檢查,但是一個有問題的基本元件,套上一個有問題的 UI,從外觀來看搞不好行為是正確的,而看不到埋在裡頭的問題。

結果就是,為了確保使用者隱私,結果犧牲了介面操作的直覺,以及 client 的測試與程式品質,做出行為又奇怪、基礎又不堅實的軟體。話說回來,後者大概也對系統安全有一定的幫助,因為會造成沒什麼使用者想用,如果是開放的 API,也沒什麼開發者想開發,怎樣的城市治安最好呢?死城。

舉個例子。美國 Yahoo! 有一套 Objective-C 的 Social SDK,看起來就沒有怎麼好好測試過—如前所述,OAuth 在登入完成後,server 會透過一組協定好的 callback URL,將 access token 傳回給 client。開發者可以選擇不要填寫這個 callback URL,在 Yahoo! 的這套 SDK 中,預設填成了「oob」,代表沒有 URL,但,如果你把這串「oob」換掉,換成你想要使用的 callback URL,居然會馬上 crash。Yahoo! 用了 OAuth,Yahoo! 自己實作一套用來通過 OAuth 認證的 SDK,做出來卻是這樣的品質。

其次,就是不但假設 client 不需要測試,甚至假設 client 都不會發生任何錯誤,不會誤解文件,不會傳錯參數。於是,你經常會遇到明明就是一份密密麻麻的 API 手冊,但就是沒告訴你一旦發生錯誤,client 端到底會看到什麼,有些更扯的連回傳資料中每個欄位到底是什麼意思都不解釋,好像只要拿到資料,就什麼人都看得懂。

實際嘗試橋接 API,就會看到各種稀奇古怪的行為。例如:

  • 分明問題出在少傳了一個 GET 參數,但是 server 卻回傳 HTTP 404 錯誤,於是你花了三個小時檢查你是不是把 base URL 給打錯了…。
  • 分明 API 成功的結果都是回傳 XML 資料,你也期待發生錯誤的時候,應該出現以 XML 呈現的錯誤訊息,但錯誤訊息偏偏是 HTML,裡頭的文字是:「如果您可以看到網頁中的訊息,請跟誰誰誰洽詢」—當你在寫一個處理 XML 的 client 的時候,會去特別打開瀏覽器看這個錯誤訊息網頁,才是怪事。而 XML 裡頭用的是 UTF-8 編碼,網頁偏偏是 Big5 編碼,還是套上了精美 CSS 的那種。
  • 發生錯誤的時候直接回傳空字串,什麼線索都沒有。有時候,狀況是 server 沒有符合搜尋條件的資料,你期待的是至少回傳一個 XML 或是 JSON,裡頭是一個空的 array,但是偏偏卻是空字串。

同時,你經常發現,當你拿到一份 API 的時候,API 的作者往往都沒有先測試過,甚至不知道可以拿什麼工具測試自己寫出來的 API,不知道原因究竟是只要 server 可以吐出東西來就算完成工作,還是用 PHP 這類的語言寫一個拿來測試 API 的 HTTP client 真的很困難。結果呢,client 軟體在介面上出現了 XML Parser error,也要先來問 client 端這邊看到什麼訊息,一看,XML 中出現了 PHP 錯誤,或是出現了不是 UTF-8 編碼的文字…。

接下來你可能會收到一串信件,大意是,最近 server 送出的資料常常出現問題,希望 client 端可以協助修改程式,讓 client 軟體在遇到問題時,可以有效協助 server 端找出問題。可是,API 本身就完全沒有規劃錯誤處理,自己也沒有測試工具,client 軟體能夠幫上什麼忙呢?

第三,是 server 端往往沒有考慮到 client 端的連線成本。桌面環境就罷了,在手機等使用行動數據網路的裝置上,也不見得什麼人都是辦吃到飽費率,每個封包都是要花通信費用,軟體從業人員不把如何節省使用者的通信費用放在設計中,實在不怎麼道德;不過,我也不知道是不是為了鼓勵使用者辦吃到飽費率,才故意這樣設計的。

有時候你就可以看到,明明就是一個 HTTP request 可以抓完的資料,偏偏要設計成好幾個 API,分好幾次抓—每次 client 端 POST 一些東西上去,每次 request 的 HTTP header,都是封包,都是新台幣啊。

就 Desktop/Mobile 軟體來說,把可以一次抓完的資料分成好幾次抓,又很容易可以把程式寫成很難懂。Desktop/Mobile 軟體在抓取網路資料的時候,不能夠 block 住 UI,所以勢必要寫成 Async 的方式。

我假設您是一位 REST API 的工程師,之前有過 Web 前端介面的開發經驗,這樣說可能會比較好懂:client 端抓取資料,都像是在 JavaScript 中發送 AJAX request 一樣,要使用 callback function 處理回傳的資料,而因為還要去抓另外一個 API 的資料,所以這個 callback funtion 裡頭還要繼續發送 request,這個 request 又要有 callback,所以 callback 裡頭又有 callback,indent 多到可能 80 個空格都塞不完需要折行,或是拆成好幾個小的 function 相互呼叫來呼叫去,最後讓人搞不清楚程式的進入點在哪裡。況且在 client 端真正可以拿來做正事的語言,要寫 callback 的時候,還不像 JavaScript 那麼好寫。

而如果這幾個 request 之間沒有相依性,不需要一個抓完才抓下一個,規劃 server API 的時候就是期待 client 端一次分送好幾個 request,把不同 endpoint 的資料抓回來,那 client 端大概就需要把這些 request 放在不同的 thread 當中,原本沒必要用到 multithreading 的程式也非得用到 multithreading 不可。如果是不同 thread 中不同 request 回傳的資料,還要合成一筆資料,那又有可能把資料寫爛,甚至程式 crash。

第四。你經常會覺得 server 端與 client 端有些地方實在沒什麼共識,例如,就 client 的角度來說,會覺得 client 寫完、QA 完成測試後,server 就不應該要有改動,因為 client 要把各種取得的資料儲存成平台的 native 格式,如果取得的資料不符合期待,該有資料的記憶體位置沒資料,很容易就會造成 p0 等級的問題—應用程式 crash。

但一些做 server 端的人,似乎是這麼想:因為 client 軟體已經散佈出去到使用者手中,想要讓全部的使用者都換成新版並不容易,但是 server 卻是在自己手裡,有什麼改動都可以立刻生效,所以從 server 下手最便捷、最省事,只要在既有的 server API 動一點手腳,不用改動 client 程式,就可以直接出現一個新功能。

於是,你不希望發生的事情,統統發生了。

你的心中於是經常迴盪這樣一首青春舞曲:

Client 寫完 server 依舊被改爛
Server 修完明天還是一樣地改
我的青春一去無影蹤
我的青春小鳥一樣不回來
我的青春小鳥一樣不回來…。

第五,或許是我個人的問題,我一看到 API 裡頭有錯字就皺眉頭。我一直在納悶,當初某個有名的大站推出 1.0 版本的 Open API 的時候,到底是在什麼狀況下,可以把「videos」拼成「videoes」。

Be Sociable, Share!

3 thoughts on “那些讓人皺眉的 REST API

Leave a Reply