最近更新: 2015-04-21

CommonGateway JSON 處理之自動回傳

在「CommonGateway 初步第二篇 - JSON 的處理與資料上傳」說明了 CommonGateway 對於 JSON 資料的處理辦法。根據早先的規則要求,客戶端若想要取得 JSON 文件,則應送出需求標頭 Accept: application/json 。而 CG 收到此需求時,會自動載入和控制項方法同名的 JSON 視圖 (.pjs),例如 index.pjs, get.pjs 。而這些 JSON 視圖的內容,往往只有一行 echo json_encode($xxx); ,如「CommonGateway 初步第二篇 - JSON 的處理與資料上傳」範例所示。

這樣的視圖內容實在太空泛了。如果我有好幾個控制項都這麼回傳 JSON 文件的話,我就要複製改名好幾個這樣的 .pjs 視圖。這工作的重複性太高了, Don't Repeat Yourself 。根據 DRY 原則, CG 在 r62 擴充了視圖處理規則,增加一條自動回傳 JSON 文件的規則。如果客戶端要求傳回 JSON 文件但設計者未建立視圖時,CG 會自動調用 json_encode() 回傳資料模型(model)。在我的使用經驗上,九成的 JSON 視圖 (.pjs),其內容都只有一行 json_encode($model) 。故將此規則寫入 CommonGateway ,減少空泛又重複的 JSON 視圖文件。

舉例來說,我設計了一個音樂播放控制項 player.php 。其方法 index() 可以取回目前的曲目清單。

<?php
class Player
{
    public function index()
    {
        // do something to get play-list.
        // for example:
        $playlist = array(
            'song1',
            'song2',
            'song3'
        );

        return $playlist;
    }
}
?>

範例 index() 方法以陣列型態回傳了一個曲目清單。在介紹 CommonGateway 時就說, CG 會自動將方法回傳值指派為 $model 變數。當客戶端要求回傳 JSON 文件時, CG 會載入和控制項方法同名的 index.pjs 視圖,交由視圖處理 $model 的回報格式和內容。在此,這個方法只是要回傳曲目清單,又資料模型已經是曲目清單了,那麼 index.pjs 只需要一行便可完成工作:

<?php
echo json_encode($model);
?>

不過這是在 r62 之前的情形。在 r62 增加自動回傳 JSON 規則之後,如果 CG 找不到 views/player/index.pjs ,就會自動調用 json_encode($model) 回傳 JSON 文件給客戶端。如果你的資料模型符合上述狀況,那麼你可以省略 JSON 視圖文件。

這項規則只有 JSON 視圖適用。其他如 XML 視圖等,並沒有 json_encode() 這麼一致的資料編碼法或序列化方法。CG 再聰明也猜不出你的 XML 結構想要長什麼樣子。如果你的 RESTful API 可提供 XML 文件的話,你還是得要自己建立 XML 視圖 (.pxml)。話雖如此,現在的 RESTful API 設計幾乎將 JSON 列為唯一的文件型態,而不處理其他文件型態。對於一個只回傳 JSON 文件的 RESTful API , CG 的 r62 擴充內容幾乎讓設計者不必建立任何視圖文件。

在一個設計規格比較寬鬆的環境中,設計者用 CG 實作 RESTfu API 時,實作好控制項方法後,可以先套用 CG 的自動回傳 JSON 規則,看看它產出的 JSON 文件的格式是否符合你的設計需求。如果不滿足的話,你才需要建立自己的 .pjs 視圖去處理回傳內容。在多數案例中,只需要實作控制項內容,而不必設計視圖,就幾乎完成一個可以用的 RESTful API 了。

樂多舊網址: http://blog.roodo.com/rocksaying/archives/44123533.html

樂多舊回應
chiu0602@gmail.com(chiu) (#comment-25320520)
Tue, 05 May 2015 14:57:06 +0800
石頭大大你好,我遇到一點困難:我傳送了一個GET request到"/index.php/main","Accept"為"application/json, text/plain, */*",專案中"main"只有"index.phtml",結果common-gateway 回傳json_encode(index.phtml),這算是bug嗎?我認為common-gateway應直接回傳index.phtml內容

再一次謝謝你的傑作!
未留名 (#comment-25320679)
Thu, 07 May 2015 10:56:59 +0800
你的 get request 是 index.php/main 。所以 CG 會載入 controllers/main.php ,配置 Main 類別的實體。
main.php 應該有 class Main 的定義。

因為沒有其他參數,所以 CG 會呼叫 main->index() 方法。
index.php/main/xxx => 呼叫 main->get('xxx');
index.php/main (後面不帶參數) => 呼叫 main->index() 。

common-gateway 只看 accept 的第一個內容,所以第二個 text/plain 無用。
根據你的 Accept 設置 application/json, CG 處理視圖時會讀 views/main/index.pjs ,而不是 views/main/index.phtml 。

如果你沒有 views/main/index.pjs , CG 就套用本文說的自動回傳機制,直接呼叫 json_encode($model) 。
ps. $model = main 實體。

所以初步看來,你得到結果不是 Bug 。

因為你沒說你的 index() 回傳了什麼,我不能判斷更多內容。
未留名 (#comment-25321034)
Mon, 11 May 2015 11:04:08 +0800
問題就是common-gateway 只看 accept 的第一個內容, 若CG能判斷index.pjs而看accept 的第二個內容, 再回傳index.phtml(若存在)就不是bug了
因為request accept應該是回傳優先次序, 對嗎?
未留名 (#comment-25321124)
Tue, 12 May 2015 11:48:11 +0800
沒有人規定服務端默認的回傳型態一定是 HTML 。你看 Apache 的文件,服務端可以指定不同的預設回傳型態。甚至可以不回報 Content-Type ,而讓客戶端自己猜收到什麼文件。http://httpd.apache.org/docs/current/mod/core.html#defaulttype

按 CG 的設計,文件型態優先回傳序列最高的是 JSON 。就算 CG 加入了 Accept 優先序列的判讀行為,若按你的 Accept , CG 還是會回報 JSON 給你。

而在 RESTful 的實務設計中,為了簡化設計內容,客戶端或服務端通常都選擇明確地提示它只要或只提供什麼樣的文件型態。以你的例子來說,你送出的 Accept 若列了多種型態,那你的 RESTful 客戶端程式在接收資料時,也必須判斷 RESTful 服務端回報的 Content-Type 的內容才能正確處理接收到的資料。但大多數的設計者,不會或根本就忘了這件事。所以常見的情形是,RESTful API 直接說我可以提供哪些型態;客戶端直接就按自己的需要索取指定的一種型態。讓彼此都省點事。

配合實務經驗,而且我也不想為這個很少使用的功能增加 CG 的程式碼行數,所以我的 CG 只看 Accept 的第一項。
ps. 我當初在規劃 Accept 優先順序的處理程式時,寫不到三分之一就不想實作它了。

說句實在話,隨著現在 RESTful API 全面向 JSON 靠攏的趨勢,我甚至覺得載入視圖的動作都可以省掉了。只要把控制項動作的回傳結果(方法回傳值) JSON 化回傳客戶端就好 (所以 CG 才有了 JSON 自動回傳規則)。