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: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: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: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:php
// 修正前
echo json_encode($results);

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


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

```php: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:js
$.ajax({
    url: 'url',
    type: 'post',
	contentType: 'application/json',
}).done(function (data, status, jqXHR) {
	// 成功時の処理
}).fail(function (jqXHR, status, error) {
	// 失敗時の処理
});
```

## 参考サイト
[【CakePHP3.x】jsonを返すapiを作りたい](http://www.aipacommander.com/entry/2016/09/12/223813)
[【CakePHP3.x】Unable to emit headers. Headers sent in file=... line=xxx](http://www.aipacommander.com/entry/2016/12/13/120641)
[ajax通信でステータスコード200が返ってきているのにerror側の処理が実行される](https://teratail.com/questions/33996)