基本
Laravel 5は、標準でメンテナンスモードへの切り替え、解除機能をもっています。 メンテナンスモードを有効にすると、全てのアクセスで503エラーとなり、以下のテンプレートを表示します。
resources/views/errors/503.blade.php
メンテナンスモードかどうかの判定は、以下のファイルで行っています。
storage/framework/down参考 メンテナンスモードの判定について
上記ファイルが存在するとメンテナンスモードとなります。 判定はミドルウェアで行われていて、app/Http/Kernel.php の $middlewareで定義されている以下になります。
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class
-
メンテナンスモードへの切り替え
メンテナンスモードへ切り替える場合、artisan downを実行します。
$ php artisan down
メンテナンスモードの解除
メンテナンスモードを解除する場合、artisan upを実行します。
$ php artisan up
IP制限
サイトを運用していると、許可IPだけに正常画面を表示し、許可IP以外にはメンテナンス画面を表示したい場合があります。 しかし、デフォルトではできないので、許可IPは通常画面を表示して、それ以外はメンテナンス画面を表示するミドルウェアを作成します。 また、許可IPは、簡単に変更できるように.envファイルで設定するようにします。
ミドルウェアは、もともと使用されている以下のファイルを参考にします。
vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/CheckForMaintenanceMode.php
-
許可IPの指定方法
-
作成
.envに新しい項目を追加します。今回は変数名を"MAINTENANCE_IP"として、許可IPをカンマ区切りで複数指定できるようにします。
// メンテナンス時にアクセス可能なIP // カンマ区切りで定義する MAINTENANCE_IP=192.0.0.10,172.16.1.10
Laravel5のミドルウェアの雛形は コマンド "artisan make:middleware"で作成できます。 今回は、元のファイルと同じ名前の"CheckForMaintenanceMode"にするので、以下のように実行しました。
$ php artisan make:middleware CheckForMaintenanceMode Middleware created successfully.すると、以下のようなファイルが作成されます。
app/Http/Middleware/CheckForMaintenanceMode.php中身は以下のようになっています。
<?php
namespace App\Http\Middleware;
use Closure;
class CheckForMaintenanceMode
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
return $next($request);
}
}
これを以下のように修正して、メンテナンスモードの時、.envの"MAINTENANCE_IP"をチェックするようにします。 アクセスしてきたクライアントIPの取得は、$request->getClientIp()で行っています。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Foundation\Application;
use Symfony\Component\HttpKernel\Exception\HttpException;
/**
* オリジナルは以下
* Illuminate\Foundation\Http\Middleware\CheckForMainenanceMode.php
*
*/
class CheckForMaintenanceMode
{
protected $app;
/**
* Create a new middleware instance.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function __construct(Application $app)
{
$this->app = $app;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($this->app->isDownForMaintenance()) {
$ip = $request->getClientIp();
$allowIp = explode(',', env('MAINTENANCE_IP'));
if (!is_array($allowIp) || !in_array($ip, $allowIp)) {
throw new HttpException(503);
}
}
return $next($request);
}
}
作成したミドルウェアのユニットテスト
ユニットテストを作成するミドルウェアのパスは以下なので、
app/Http/Middleware/CheckForMaintenanceMode.php以下のコマンドを実行して、雛形を作成します。
$ php artisan make:test Http/Middleware/CheckForMaintenanceModeTest参考 Laravel 5 ユニットテスト・雛形作成
すると、以下のファイルが作成されます。
tests/Http/Middleware/CheckForMaintenanceModeTest.php
作成されたファイルを修正して以下のようにします。 メンテナンスモードの切り替えは、storage/framework/down ファイルを直接操作するのではなく、 コマンドラインからの切り替えに使用されている、UpCommandとDownCommandを使いました。 また、許可IPは、app/.envに設定している値を使うので、.envで設定しておく必要があります。
<?php
use App\Http\Middleware\CheckForMaintenanceMode;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Http\Request;
use Illuminate\Foundation\Console\UpCommand;
use Illuminate\Foundation\Console\DownCommand;
use Symfony\Component\HttpFoundation\ServerBag;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\NullOutput;
class CheckForMaintenanceModeTest extends TestCase {
protected $target;
protected $input;
protected $output;
protected $downCommand;
protected $upCommand;
public function setUp()
{
parent::setUp();
$this->target = new CheckForMaintenanceMode($this->app);
$laravel = $this->app->make('Illuminate\Contracts\Foundation\Application');
$application = $this->app->make('Symfony\Component\Console\Application');
$this->upCommand = new UpCommand();
$this->upCommand->setLaravel($laravel);
$this->upCommand->setApplication($application);
$this->downCommand = new DownCommand();
$this->downCommand->setLaravel($laravel);
$this->downCommand->setApplication($application);
$this->input = new ArgvInput();
$this->output = new NullOutput();
}
public function testHandler()
{
// 許可IP
$menteIps= explode(',', env('MAINTENANCE_IP'));
$allowIp = $menteIps[0];
$this->assertRegExp('/^\d+\.\d+\.\d+\.\d$/', $allowIp);
// 許可されていないIPアドレス
$otherIp = '10.254.254.254';
$this->assertNotEquals($otherIp, $allowIp);
$next = function($request) {
return 'test ok';
};
$request = new Request();
//***********************************
// メンテナンス解除
$this->upCommand->run($this->input, $this->output);
// メンテナンスでない場合、許可IP以外からアクセス可能であること
$request->server = new ServerBag(['REMOTE_ADDR' => $otherIp]);
$result = $this->target->handle($request, $next);
$this->assertEquals('test ok', $result);
// メンテナンスでない場合、許可IPからアクセス可能であること
$request->server = new ServerBag(['REMOTE_ADDR' => $allowIp]);
$result = $this->target->handle($request, $next);
$this->assertEquals('test ok', $result);
//***********************************
// メンテナンス中に変更
$this->downCommand->run($this->input, $this->output);
// メンテナンス中の場合、許可IPからのアクセスはエラーにならないこと
$request->server = new ServerBag(['REMOTE_ADDR' => $allowIp]);
$result = $this->target->handle($request, $next);
$this->assertEquals('test ok', $result);
// メンテナンス中の場合、許可IP以外からのアクセスは 503エラーになること
$request->server = new ServerBag(['REMOTE_ADDR' => $otherIp]);
try {
$this->target->handle($request, $next);
$this->fail();
} catch (Symfony\Component\HttpKernel\Exception\HttpException $e) {
$this->assertEquals(503, $e->getStatusCode());
}
//***********************************
// メンテナンス解除
$this->upCommand->run($this->input, $this->output);
// メンテナンス解除後、許可IP以外からアクセス可能であること
$request->server = new ServerBag(['REMOTE_ADDR' => $otherIp]);
$result = $this->target->handle($request, $next);
$this->assertEquals('test ok', $result);
}
}
参考
-
メンテナンスモードの判定について
メンテナンスモードの判定は、下記のファイルに定義されている isDownForMaintenanceメソッドで行っています。
/vendor/laravel/framework/src/Illuminate/Contracts/Foundation/Application.php
中身は以下のようになっていて、storageパス以下の"framework/down"というファイルが存在すればメンテナンスモードと判定しています。
public function isDownForMaintenance()
{
return file_exists($this->storagePath().'/framework/down');
}