laravel5.2でCORS対応のRESTfulAPIを作成する

煩雑だったりなかなかデンジャーな方法をとってるケースが見受けられるので、おそらく問題は少ないだろうと思われる方法を紹介します。




やりたいこと



https://AAAA.jpx から https://BBBB.jpx へREST形式でGETしたりPOSTを送りたい。



RESTfulAPI について



URLをリソースとして表現してアドレスによってデータの取得、作成、削除、更新などを行えるようにする設計思想
https://example.com/api/create
へPOSTリクエストとリソースを作成する。
例えばレスポンスとして作成されたリソースのid:123が返ってくるなど。

https://example.com/api/get/123
へGETリクエストを送るとid:123のリソースを取得する。

https://example.com/api/update/123
へPUTリクエストを送るとid:123のリソースを更新する。

https://example.com/api/delete/123
へDELETEリクエストを送るとid:123のリソースを削除する。

といった感じになる。
割と雑な説明なので注意。



CORS (Cross-Origin Resource Sharing)について



最近のWebブラウザは悪いことができないようにSame-Origin Policyが適用されている。
これが適用されている場合ドメインが違うとアクセスに制限がかかる。
ドメインだけでなくポートが違うだけでもアクセスすることができない。

※スキーム(httpとhttpsなど)が違う場合もアクセスできないという解説が見受けられるがそもそも使用しているポートが違うので分ける必要があるかは不明



サンプルソース


基本はレスポンスにヘッダを2つ追加するといい
例えばnode.js * express だと

router.get("", /* @callback */ function(req, res) {
 
 res.header("Access-Control-Allow-Origin", "*");
 res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
 
 res.send("Hello REST Api");
});

といった感じで簡単にCORS対応が可能(上記ソースは多少問題がある)だがlaravelだとルーティング周りでいろいろやっているので一手間必要になる。

1. Controller作成

ターミナルからプロジェクトルートで以下のコマンドを叩くことでコントローラが作成できる。
そのまま作ってもいい。
php artisan make:controller RestController --resource


...projectDir\Http\Controllers\RestController.php
に作成されるはず
getやpostをするためのメソッドが一通り作られる。

2. routes.phpに追記

cors対応を行う。
実際は記述箇所はどこでもいいが一応ファイルの先頭へ下記を追記する。
header('Access-Control-Allow-Origin: *');
header( 'Access-Control-Allow-Headers: Authorization, Content-Type' );

「こんなところに書いて大丈夫なの?」という疑問が湧くかもしれない(実際私がそう思ったけど)
意味合いとしては全体を外部からのアクセスを許可するというということだが後述のCSRF対策があるので問題ないはず。

3. routes.phpにルートを追記

下記はGET,POST,PUT,DELETEを定義しているが必要なものだけでもいい
Route::group(['prefix' => 'api'], function () {
 Route::resource('xxxx', 'XXXXController');
});
URLは
webRoot/api/xxxx になる

ルートの状態はプロジェクトのルートで下記のコマンドを入力することで確認できる。
php artisan route:list

+--------+-----------+----------------------+------------------+---------------------------------------------+------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+-----------+----------------------+------------------+---------------------------------------------+------------+
| | GET|HEAD | / | | Closure | web |
| | POST | api/rest | api.rest.store | App\Http\Controllers\RestController@store | web |
| | GET|HEAD | api/rest | api.rest.index | App\Http\Controllers\RestController@index | web |
| | GET|HEAD | api/rest/create | api.rest.create | App\Http\Controllers\RestController@create | web |
| | DELETE | api/rest/{rest} | api.rest.destroy | App\Http\Controllers\RestController@destroy | web |
| | PUT|PATCH | api/rest/{rest} | api.rest.update | App\Http\Controllers\RestController@update | web |
| | GET|HEAD | api/rest/{rest} | api.rest.show | App\Http\Controllers\RestController@show | web |
| | GET|HEAD | api/rest/{rest}/edit | api.rest.edit | App\Http\Controllers\RestController@edit | web |
+--------+-----------+----------------------+------------------+---------------------------------------------+------------+

4. クロスサイトリクエストフォージェリ(CSRF)対策の対策

以上でいけるかと思いきやエラーになる。
CSRFチェックから除外するURLを記述(Laravel5.1以降のみ)
下記はwebRoot/api以下を全て除外
App/Http/Middleware/VerityCsrfToken.php

    protected $except = [
        'api/*',
    ];

ここまででやったことをまとめると全体に外部からアクセス可能なようにヘッダを追加して、
RestfulAPI用にルートを作成して、
webRoot/api以下をCSRFチェックから除外した と言った感じになる。

これで外部からリクエストを投げることが可能になります(取扱注意!!)



メモ


5.1未満が CSRFの除外設定ができないので一手間いるかも
5.3以降はルーティングの方法が変わっているが、ここにある情報で十分対応可能かと(apiっていうミドルウェアが追加されているのでそれ使うだけでいいかもしれない)



参考



REST APIとは? – API設計のポイント!
http://wp.tech-style.info/archives/683

CORSまとめ
http://qiita.com/tomoyukilabs/items/81698edd5812ff6acb34

cors - Laravel5 + AngularJS で作るSPA
http://qiita.com/fluke8259/items/c884bada22ccd286cf48

Laravel - RESTful
http://qiita.com/fluke8259/items/23e833ed327c10fcace0

Laravel5.0 CSRFチェックを無効化
http://qiita.com/rana_kualu/items/3f9d0d6b9a363fd2108e

2016年12月2日金曜日