LoginSignup
51

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-08-13

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

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

の2つです。

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

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

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

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

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

つまり、

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

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

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

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

php artisan make:request PostRequest

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

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

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

<?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);
    }
}

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

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

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

メモ

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

<!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>

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
51