CakePHP3でJSONを返すだけのAPIを実装した時に起きた問題


発生した問題

Cakeでjsonを返して、jQueryで処理しようとしたら、下記の問題発生

  • HTTPステータスが200(OK)でなのに、$.ajaxのfailに分岐してしまう
  • Responseにechoしたjson以外のものが混じっている

HTTPステータスが200(OK)なのに、$.ajaxのfailに分岐してしまう

failのstatusを見てみたら、parsererrorが返ってきていた。

  • Cake側で余計なheaderを付与していたこと
  • jQuery側で余計なdataTypeを指定していたこと

が問題っぽい。

php
if($this->request->is('ajax')) {
    $query = $this->モデル名->find('all');
    $results = $query->all();
    //header('content-type: application/json; charset=utf-8'); この行が不要
    echo json_encode($results);
    return;
}
js
$.ajax({
    url: 'url',
    type: 'post',
    contentType: 'application/json',
    //dataType: 'json' この行が不要
}).done(function (data, status, jqXHR) {
    // 成功時の処理
}).fail(function (jqXHR, status, error) {
    // 失敗時の処理
});

上記のコメントアウトした行を削除することで、成功時の処理へ行くようになった。
サーバ側のMINEタイプとdataTypeの不整合で問題が起きていたっぽい?
jQuery側で自動判別してもらうことで解決した模様。

Responseにechoしたjson以外のものが混じっている

Chromeの開発者ツール - Network - Responseを見てみた。
するとjson以外に下記が入っていた。

HTMLタグ

<div>
            <br>
        <br>
        <br>
        <br>
        <br>        
        <button id="btn-ajax" type="submit">ajax</button></div>

なんかHTMLのタグが入ってるー
なんとなくこれはCakePHPのViewが入っちゃってるんじゃなかろうかと思い、
jsonを返す場合はレイアウトを出力しないように修正してみる

php
if($this->request->is('ajax')) {
    $this->viewBuilder()->autoLayout(false); // この行を追加
    $this->autoRender = false;               // この行を追加
    $query = $this->モデル名->find('all');
    $results = $query->all();
    echo json_encode($results);
    return;
}

警告文

Warning (512): Unable to emit headers. Headers sent in file=/Applications/MAMP/htdocs/cakephp3/src/Controller/xxxxxxxxxxxxxxxxxxxx.php line=193 [CORE/src/Http/ResponseEmitter.php, line 48]
Warning (2): Cannot modify header information - headers already sent by (output started at /Applications/MAMP/htdocs/cakephp3/src/Controller/xxxxxxxxxxxxxxxxxxxx.php:193) [CORE/src/Http/ResponseEmitter.php, line 148]
Warning (2): Cannot modify header information - headers already sent by (output started at /Applications/MAMP/htdocs/cakephp3/src/Controller/xxxxxxxxxxxxxxxxxxxx.php:193) [CORE/src/Http/ResponseEmitter.php, line 181]
Warning (2): Cannot modify header information - headers already sent by (output started at /Applications/MAMP/htdocs/cakephp3/src/Controller/xxxxxxxxxxxxxxxxxxxx.php:193) [CORE/src/Http/ResponseEmitter.php, line 181]

なんかWarningとか出てるー
なんとなくだけど何も設定してないから、echoしたやつがheaderに入っちゃってるのかな?
下記のように修正で解決

php
// 修正前
echo json_encode($results);

// 修正後
$this->response->body(json_encode($results));

最終的なコード

最終的に下記のようなコードにしたら上手くいった

php
if($this->request->is('ajax')) {
    $this->viewBuilder()->autoLayout(false);
    $this->autoRender = false;
    $this->response->charset('UTF-8');
    $this->response->type('json');
    $query = $this->モデル名->find('all');
    $results = $query->all();
    $this->response->body(json_encode($results));
    return;
}
js
$.ajax({
    url: 'url',
    type: 'post',
    contentType: 'application/json',
}).done(function (data, status, jqXHR) {
    // 成功時の処理
}).fail(function (jqXHR, status, error) {
    // 失敗時の処理
});

参考サイト

【CakePHP3.x】jsonを返すapiを作りたい
【CakePHP3.x】Unable to emit headers. Headers sent in file=... line=xxx
ajax通信でステータスコード200が返ってきているのにerror側の処理が実行される