最近更新: 2015-04-20

PHP框架 - CommonGateway 控制項動作函數回傳狀態碼的作法

CommonGateway 是我設計的一個 PHP 迷你框架。主要用於實作 RESTful API ,兼差網站設計。

在 CommonGateway 初期版本,如果你的控制項方法成功執行但不需要回傳資料文件時,你的方法應回傳 false ,讓 CG 不必繼續處理視圖工作。但這項要求違反一般函數的慣例,因為回傳 false 通常表示動作失敗。而在本文描述的情況中,你的控制項方法實際上成功了,卻要回傳 false ,語意有重大矛盾。

仔細探討 HTTP 和 CGI 規範,當一項客戶端要求圓滿完成時,至少要回傳一個表示成功的狀態碼標頭,例如常見的 “Status: 200” 。而 CG 實際上也是這麼做。當控制項方法正常地回傳 false 時, CG 視為控制項成功執行但不必載入視圖,並默認回報客戶端狀態碼 200 。

考量回傳 false 形成語意矛盾, CG 擴充了一項規則:當控制項方法回傳介於 200~599 之間的整數 (必須是整數,而不是字串),就將此回傳值視為控制項方法要回報給客戶端的狀態碼。並且認為這是一個只需要回報狀態碼而沒有後續內容的動作,故也不繼續載入視圖。如此一來,就可以在方法的回傳動作中明確表達設計意圖,同時讓 CG 理解此動作不必載入視圖。


class Player
{
    // Before
    public function pause()
    {
        // Do something to pause player.

        // nothing to render. CG response 200 (OK) by default.
        return false;
    }

    // Now
    public function pause()
    {
        // Do something to pause player.

        // controller response 200 (OK). no addition content.
        return HttpResponse::OK;
    }
}

比較上例兩個新舊寫法,新寫法更直觀,語意更明確。

接著說明控制項方法回傳錯誤狀態的方法。根據 HTTP 規範,狀態碼 300 ~ 599 之間的數值,都是錯誤。從程式語言的觀點,這類狀態碼應視為例外錯誤。故 CG 提供了方法 HttpResponse::exception() 給控制項回報狀態。

例如參數不符時,應回報狀態 400 (Bad Request) ,那控制項就呼叫 HttpResponse::exception(HttpResponse::BAD_REQUEST);。此方法會提前中止程式,並回報狀態碼給客戶端。此時因為控制項方法不是正常返回 CG ,故 CG 就不會載入視圖。

但是 HttpResponse::exception() 表達方式太冗長了。 HttpResponse:: 重複了兩次,天啊。CG 的 HttpResponse 也提供了下列回應方法:

  • HttpResponse::badRequest($msg=false)
  • HttpResponse::unauthorized($msg=false)
  • HttpResponse::forbidden($msg=false)
  • HttpResponse::notFound($msg=false)
  • HttpResponse::internalServerError($msg=false)

完整列表請看 CommonGateway 技術文件:HTTP 狀態碼與方法

因此舊寫法,如 HttpResponse::exception(HttpResponse::NOT_FOUND); ,現在可以寫成 HttpResponse::notFound();。意義不變,長度少一半。當然寫成 return HttpResponse::NOT_FOUND; 也一樣。但是回應方法可以接受一個狀態訊息參數,這是回傳值做不到的事。


class Player
{
    // Before
    /**
    @param $track_number should >= 1.
     */
    public function play_track($track_number)
    {
        $number = intval($track_number);
        if ($number <= 0) {
            HttpResponse::exception(HttpResponse::BAD_REQUEST, "Bad Track Number");
        }

        // ...
    }

    // Now
    public function play_track($track_number)
    {
        $number = intval($track_number);
        if ($number <= 0) {
            HttpResponse::badRequest("Bad Track Number");
            // or
            return HttpResponse::BAD_REQUEST;
        }

        // ...
    }
}

附帶一提, CG 會檢查參數數量是否滿足控制項方法宣告。如果參數不夠,會直接回報狀態 400 (Bad request) 。所以範例的 play_track() 方法不需要先確認參數數量,則可以直接檢查 $track_number 的內容。

最後補充 HTTP 規範關於狀態碼的兩件事。

一、狀態碼 2xx ,如 200 (OK), 202 (ACCEPTED), 204 (NO_CONTENT) 等,都代表成功。在實務上,實作 RESTful API 時,一般都用 200 (OK) 概括。而 RESTful 客戶端也習慣以 200 判斷是否成功。不過有些客戶端甚至把其他 20x 狀態碼當成錯誤,這可不算好設計。

二、狀態碼標頭除了代碼部份,還可以接續一行狀態訊息。所以完整的狀態碼標頭應該像 “Statue: 200 OK” 。但有些 http 客戶端函數庫會直接忽略後續的狀態訊息,只讓設計者取得狀態代碼。而有些則可讓設計者取得這兩部份。以 JavaScript 中的 XMLHttpRequest API 為例, XMLHttpRequest.status 屬性只表示狀態代碼,例如 200 ;而 XMLHttpRequest.statusText 屬性則包含整行訊息,例如 “200 OK” 。

CG 的回應方法因此提供了一個狀態訊息參數,讓設計者可以更詳細地表達控制項的錯誤訊息。若未指定訊息參數,則 CG 會自行填入預設的狀態訊息。

相關文章