最近、Laravel の勉強をしていて遭遇したエラーをまとめておきます。うっかりミスでも、情報がないと、意外と原因に気づくのに時間がかかるもので・・・。
まとめ
- HTTP ステータスコードが 405 の場合
- ルーティングの定義漏れ
- HTTP ステータスコードが 419 の場合
- CSRF 対策漏れ
前提
- Laravel 5.7
405
- 別名 「MethodNotAllowedHttpException」
事象
例えば、routes/web.php
で、/auth/login
という URL に対して、以下のように定義されている場合。
// GET メソッドは定義済
Route::get('/auth/login', 'LoginController@show');
// POST メソッドは未定義
// Route::post('/auth/login', 'LoginController@authenticate');
この場合に、POST メソッドにアクセスすると、405 エラーが発生します。1
解決策
ルーティングを正しく定義しましょう。
// GET メソッドは定義済
Route::get('/auth/login', 'LoginController@show');
// POST メソッドも定義する
Route::post('/auth/login', 'LoginController@authenticate');
419
事象
例えば、Blade テンプレートで、以下のようなログインフォームを作成した場合。
<form action="/auth/login" method="post">
<div class="form-group">
<label for="email">メールアドレス</label>
<input type="email" name="email" class="form-control" value="{{ old('email') }}">
</div>
<div class="form-group">
<label for="password">パスワード</label>
<input type="password" name="password" class="form-control">
</div>
<button type="submit" class="btn btn-primary">ログイン</button>
</form>
この場合に、「ログイン」ボタンを押すと、419 エラーが発生します。
解決策
Blade テンプレートに CSRF 保護 を追加します。
具体的には、@csrf
を追加するだけです。
<form action="/auth/login" method="post">
<!-- CSRF保護 -->
@csrf
<div class="form-group">
<label for="email">メールアドレス</label>
<input type="email" name="email" class="form-control" value="{{ old('email') }}">
</div>
<div class="form-group">
<label for="password">パスワード</label>
<input type="password" name="password" class="form-control">
</div>
<button type="submit" class="btn btn-primary">ログイン</button>
</form>
与太話
@csrf
が追加されたのは Laravel 5.6 からです。5.5 以前は {{ csrf_field() }}
でした。5.6 以降でも {{ csrf_field() }}
も使えるようですが、個人的には @csrf
のほうが覚えやすくていいかなと思います。
また、同様に、PUT・PATCH・DELETE メソッドを使う際に記述していた {{ method_field('PUT') }}
も、@method('put')
が追加されています。
あとがき
公式ドキュメントに一言書いてあると嬉しかったですね。いや、これぐらい、すぐ分かれよ(そもそも、そんなミスするなよ)という話なのかもしれませんが。
参考
おまけ
どうやって POST メソッドをテストすればいいんだろう? と少し悩んでしまいました。
最初は Request
のモックを作るのかなと思いきや、公式ドキュメント にちゃんと注意書きがありました。
Note: Requestファサードをモックしてはいけません。代わりに、テスト実行時はgetやpostのようなHTTPヘルパメソッドへ、望む入力を引数として渡してください。
例えば、「POST メソッドで情報を送信し、正しくリダイレクトされたか?」を確かめたい場合は、以下のように post()
を使えば良いらしいです。
class LoginControllerTest extends TestCase
{
// テストで入れたデータはテスト後に削除する
use RefreshDatabase;
/**
* @test
*/
public function ログインに成功すること()
{
// ユーザーを1人作成する
$users = factory(\App\User::class, 1)->create();
// POSTで送信したい情報を第2引数に渡す
$response = $this->post('/auth/login', [
'email' => $users[0]->email,
'password' => 'secret',
]);
// リダイレクトが成功したかを確認する
$response->assertRedirect('/');
}
}
ちなみに、これを実行して 405 エラーが出ると、こんな感じになります。
-
ちなみに、GET メソッドも定義されていない(
/auth/login
に対して全く定義されていない)場合は、みんな大好き、404 エラーが発生します。 ↩