Laravel5.1.xでAPIを作る際に気になっていたことを調べました


Laravel5でAPIを作成するとき、個人的に気になっていたことは、

1. CSRFの回避
2. FormRequestの利用

の２つです。

1.はLaravelでは、POSTは原則tokenが要求されるので、それをどうしたものか？という悩みです。Middlewareで実装されているので外してしまうのは簡単なのですが、一方でCSRFのリスクは増します。Laravel5.1.xからは、一部のルートを除外できる機能が付いたのでそれを試します。

2.FormRequestは、Laravel5.xから採用された機能で、バリデーション等をFormRequestに記述することでコントローラーが非常にスッキリしていいのですが、エラー時の対応などが自動化（隠蔽）されており、API時にどう利用したらよいか、ずっと気になっていたので動作を確認してみます。

##csrfの回避(except)

Laravel5.1.xから、exceptで、除外したいrouteを記述すればよいことになりました。

例えば、http//hostname/apiを除外したい場合、Http/Middleware/VerifyCsrfToken.phpに、

```php
<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;

class VerifyCsrfToken extends BaseVerifier
{
    protected $except = [
        'api'
    ];
}
```

と記述すればよいようになりました。'api/*'のようにワイルドカードも使えるようです。
試したところ、tokenが要求されることがなくなりました。

##FormRequestを利用したValidation

以前からのValidationでは、明示的にif($validation->fails())等で、処理を分岐し、また、分岐後の処理も明示的に記述していたいので（逆に言えば、記述しなければならない）特に問題はなかったのですが、FormRequestは、そのあたりが隠蔽されているので、動作のチェックをします。

まず、通常のバリデーション。

###通常のValidation

```php
    public function store(Request $request)
    {
        //バリデーション対象
        $inputs = $request->all();

        //ルール
        $rules = [
            'name'=>'required',
            'email'=>'required|email',
        ];

        //バリデーション
        $validation = \Validator::make($inputs,$rules);

        //外部からの処理を受ける（jquery用）
        $headers = [
            'Access-Control-Allow-Origin' =>' *',
        ];

        //評価
        
        //エラーの時
        if($validation->fails())
        {
            $response["status"] = "NG";
            return \Response::json($response,'200',$headers);
        }
			
		 //正常の時
        $response["status"] = "OK";
        return \Response::json($response,'200',$headers);

    }
```

まあ、何をしているかわかりやすいといえばわかりやすいです。

>なお、ここではクロスドメインでのリクエストを許可するために、ヘッダに、Access-Control-Allow-Orignを追加し、どのドメインからのリクエストも許可しています。

###FormRequest

FormRequestを利用すると、少なくともコントローラー部は一気にシンプルになります。
下記が、同じ部分のコントローラーです。

```php
    public function store(\App\Http\Requests\PostRequest $request)
    {
        $response["status"] = "OK";
        return \Response::json($response,'200',$headers);
    }
```

ここでは、\App\Http\Requests\PostRequestを定義し、インジェクションしています。
記述はシンプルなのですが、バリデーション等が、いつ、どこで行われ、その結果どうなるのかが全く？？？となります。特に、異常時の処理は一切記述されない（できない）ため、？？？となります。
通常のフォームで利用した場合、エラー時には、自動的に各種パラメーターを付与した状態で、リクエスト元にリダイレクトされます（リダイレクト元では、パラメータを利用してエラー処理とかをします）。

つまり、

```php
return redirect()->back()->withErrors()->withInput();
```

が実行されています。
要は、APIでの利用の場合、これがどうなるのか？ということです。


結論から言えば、RequestFormのresponse()メソッドをオーバーライドして必要な記述をすれば良いようです。

FormRequest自体は、artisanコマンドで生成できます。

```php
php artisan make:request PostRequest
```

必要な内容を記述します。通常は、

* authorizeの戻り値をtrueに。
* rulesを定義

だけですが、ここでは、response()を定義（オーバーライド）し、エラー時の処理を記述しています。

```php
<?php

namespace App\Http\Requests;

use App\Http\Requests\Request;

class PostRequest extends Request
{
    //認証（これは基本trueにしてやる）
    public function authorize()
    {
        return true;
    }

    //ルール
    public function rules()
    {
        return [
            'name'=>'required',
            'email'=>'required|email',
        ];
    }

    //メッセージも書けます


    //エラー時の処理
    public function response(array $errors)
    {
        $headers = [
            'Access-Control-Allow-Origin' =>' *',
        ];

        $response["status"] = "NG+";
        $response["message"] = $errors;

        return \Response::json($response,200,$headers);
    }
}
```

これで完了です。
ちなみに、上記の記述だと、下記のようなレスポンスが得られます。

```js
{"status":"NG+","message":{"name":["The name field is required."],"email":["The email must be a valid email address."]}}
```

メッセージは、フォームの時と同じ内容となります。
RequestFormのソースコードを見る限りでは、ajaxやheaderの内容を見て自動的にjsonを返すようになっているようですが、その状態でそのまま使えることはないかなという感じなので、カスタマイズの方法を調べてみました。

##メモ

ajaxのテストに利用したクライアント側のコードも参考までに（一部環境依存部あり）。

```html
<!doctype html>
<html lang="ja">
<head>
	<meta charset="utf-8">
	<title>test</title>
</head>
<body>
<input type="button" name="button" value="push" id="btn">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script>
$(function(){
	$("#btn").click(function(){
		$.ajax({
			type:"post",
			url:"http://localhost:8000/api",
			data:{"name":"hoge","email":"hoge@hoge.com"},
			success:function(data){
				alert(data.status);
			}
		});
	});
});
</script>
</body>
</html>
```