FuelPHPをやってみる (12) - Controller_Restを使う -

2012-11-15T00:00:00+00:00 FuelPHP PHP

Restfulサポートなコントローラー機構なController_Restを使ってみる。

fuel/app/classes/controller/sample.php

<?php

class Controller_Sample extends Controller_Rest {

    /* XMLフォーマットの場合のルートノード名。
    protected $xml_basenode = "data";
    */

    // レスポンスデータが無い場合のステータスコード。デフォルトは204だったはず
    protected $no_data_status = 404;

    // メソッドが無い場合のステータスコード。デフォルトは405だったはず
    protected $no_method_status = 404;

    /* 使用するデータフォーマットで固定する。以下を設定するとレスポンスデータはJSONでエンコードされる
    protected $format = "json";
    */

    /* サポートするデータフォーマット */
    protected $_supported_formats = array(
        "html" => "text/html",
        "json" => "application/json"
    );

    public function get_index() {
        // indexアクションでHTMLなレスポンスを返す際には必ずResponse::forgeしてから返す
        return Response::forge(View::forge("sample/index"));
    }

    public function get_list() {
        if ($this->format === "html") {
            throw new HttpNotFoundException();
        }

        // responseメソッドで返すと引数に指定したデータがフォーマットに応じて変換されてレスポンスとして返される
        return $this->response(
            array(
                "message" => "ほげ"
            )
        );
    }

    public function get_delete($id = 0) {
        if ((int)$id > 0) {
            return Response::forge((int)$id);
        }

        throw new HttpNotFoundException();
    }
}

indexアクションでResponse::forgeしないといけない(View::forgeだけではダメ)な理由として、fuel/core/classes/controller/rest.phpで

public function after($response) {
if (is_array($response)) {
    $response = $this->response($response);
}

if ( ! $response instanceof Response) {
    $response = $this->response;
}

return parent::after($response);
}

となっているけど、アクションから返した値がResponse型じゃない場合には$this->responseをレスポンスとして置き換えて返すような仕組みになっている。でもし、return View::forgeしたとしても$this->responseは(恐らくは)nullにしかならない。なのでもしHTMLなビューを返す場合にはView::forgeした値をResponse::forgeしておく必要があるんじゃないかと

fuel/app/views/sample/index.php

<html>
<body>
<script type="text/javascript">
var xhr = new XMLHttpRequest();
xhr.open("GET", "/sample/list", true);
xhr.setRequestHeader("Accept", "application/json"); // Acceptを設定してapplication/jsonで取得する前提を設定しておく
xhr.onload = function() {
    if (xhr.status !== 200) {
        console.log("ERROR");

        return;
    }

    var data = JSON.parse(xhr.responseText);
    console.log(data);
};
xhr.send();
</script>
</body>
</html>

今回の場合get_listが発生するURLに直接アクセスしても、HTMLフォーマットは許容しない(HttpNotFoundExceptionをスローするようにしている)ので、XMLHttpRequest等でも同様にAcceptヘッダーを設定しないと404にしかならない。実際的にはHTMLも許容するけど、JSONを要求する場合とはレスポンス形式を変えるっていうのが良いのも知れないですけど

まぁという感じで、Ajax等で利用するデータ等を自動でエンコードしてくれたりする。んで出てくるのがこれどうやってテストやるのかって話

fuel/app/tests/controller/sample_test.php

<?php

class Test_Controller_Sample extends TestCase {
    public function test_list() {
        $driver = Request::forge("http://localhost/sample/list", "Curl");
        $driver->set_mime_type("json");

        // これを設定するとレスポンスボディがデコードされずに生データを取得できる。設定しなかったら自動でレスポンスはデコードされる模様
        // $driver->set_auto_format(false);

        $res = $driver->execute()->response();
        $this->assertEquals(200, $res->status);
        $this->assertEquals(
            array("message" => "ほげ"),
            $res->body
        );

        $res_info = $driver->response_info();
        $this->assertEquals("application/json", $res_info["content_type"]);
    }
}

indexな所のテストは省略。Request_Curlドライバを使ってテストをする。でresponse_infoメソッドを使えばレスポンスヘッダー類なデータが取得できるので、それを利用してレスポンスヘッダーが正しいかを検証する事も可能

あとset_mime_typeで取得するフォーマット方式を変えたり出来るのでそれを用いてテストする。今回の場合指定しなかったら404になるのでこのテストはfailする

っていう感じっぽいっすね。微妙に雑に書きましたけど

追記

書くの忘れてましたが、fuel/app/config/rest.phpに

<?php

return array(
    "default_format" => "html",
    "xml_basenode" => "xml",
    "realm" => "REST API",
    "auth" => "",
    "valid_logins" => array("admin" => "1234"),
    "ignore_http_accept" => false,
);

な設定とか出来る模様。

FuelPHPをやってみる (13) - Controller_Hybridを使う - jetty-maven-plugin+selenium-maven-pluginでintegration-test