HTTP-посредники (англ. middleware) предоставляют удобный механизм для фильтрации HTTP-запросов вашего приложения. Например, в Laravel есть посредник для проверки аутентификации пользователя. Если пользователь не аутентифицирован, посредник перенаправит его на экран входа в систему. Если же пользователь аутентифицирован, посредник позволит запросу пройти далее в приложение.
Конечно, посредники нужны не только для авторизации. CORS-посредник может пригодиться для добавления особых заголовков ко всем ответам в вашем приложении. А посредник логов может зарегистрировать все входящие запросы.
В Laravel есть несколько стандартных посредников, включая посредники для обслуживания, аутентификации, CSRF-защиты и многие другие. Все они расположены в каталоге app/Http/Middleware.
Создание посредника
Чтобы создать посредника, используйте команду Artisan make:middleware:
+ 5.2
добавлено в 5.2 (08.12.2016)
php artisan make:middleware AgeMiddleware
+ 5.1 5.0
добавлено в 5.1 (19.06.2016) 5.0 (08.02.2016)
php artisan make:middleware OldMiddleware
Эта команда поместит новый класс AgeMiddleware (для версии 5.1 и ранее OldMiddleware) в ваш каталог app/Http/Middleware. В этом посреднике мы будем пропускать только те запросы, в которых age будет больше 200, а во всех остальных случаях будем перенаправлять пользователей на «home» URI.
<?php
namespace App\Http\Middleware;
use Closure;
class AgeMiddleware
// для версии 5.1 и ранее
// class OldMiddleware
{
/**
* Выполнение фильтра запроса.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($request->input('age') <= 200) {
return redirect('home');
}
return $next($request);
}
}
Как видите, если переданный age меньше или равен 200, то посредник вернёт клиенту переадресацию, иначе, запрос будет передан далее в приложение. Чтобы передать запрос дальше в приложение (позволяя посреднику «передать» его), просто вызовите замыкание $next с параметром $request.
Проще всего представить посредника как набор «уровней», которые должен пройти HTTP-запрос, прежде чем он дойдёт до вашего приложения. Каждый уровень может проверить запрос и даже вовсе отклонить его.
Посредник «до» и «после»
Момент, в который сработает посредник — до или после запроса, зависит от него самого. Например, этот посредник выполнит некоторую задачу прежде, чем запрос будет обработан приложением:
<?php
namespace App\Http\Middleware;
use Closure;
class BeforeMiddleware
{
public function handle($request, Closure $next)
{
// Выполнение действия
return $next($request);
}
}
А этот посредник выполнит задачу после того, как запрос будет обработан приложением:
<?php
namespace App\Http\Middleware;
use Closure;
class AfterMiddleware
{
public function handle($request, Closure $next)
{
$response = $next($request);
// Выполнение действия
return $response;
}
}
Регистрация посредника
Глобальный посредник
Если вы хотите, чтобы посредник запускался для каждого HTTP-запроса в вашем приложении, добавьте этот посредник в свойство $middleware класса app/Http/Kernel.php.
Назначение посредника для маршрутов
Если вы хотите назначить посредника для конкретных маршрутов, то сначала вам надо добавить сокращённый ключ посредника в класс app/Http/Kernel.php. По умолчанию свойство $routeMiddleware этого класса содержит записи посредников Laravel. Чтобы добавить ваш собственный посредник, просто добавьте его к этому списку и присвойте ему ключ на свой выбор. Например:
// в классе App\Http\Kernel... protected $routeMiddleware = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, //для версии 5.2 и выше: 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, ];
Когда посредник определён в HTTP-ядре, вы можете использовать ключ middleware в массиве параметров маршрута:
Route::get('admin/profile', ['middleware' => 'auth', function () {
//
}]);
Используйте массив для назначения нескольких посредников для маршрута:
Route::get('/', ['middleware' => ['first', 'second'], function () {
//
}]);
Вместо использования массива вы можете использовать сцепку метода middleware() с определением маршрута:
Route::get('/', function () {
//
})->middleware(['first', 'second']);
+ 5.2
добавлено в 5.2 (08.12.2016)
При назначении посредника вы можете также передать полное имя класса:
use App\Http\Middleware\FooMiddleware;
Route::get('admin/profile', ['middleware' => FooMiddleware::class, function () {
//
}]);
Группы посредников
Иногда бывает полезно объединить несколько посредников под одним ключом, чтобы проще назначать их на маршруты. Это можно сделать при помощи свойства $middlewareGroups вашего HTTP-ядра.
Из коробки в Laravel есть группы посредников web и api, которые содержат те посредники, которые часто применяются к маршрутам веб-UI и API:
/** * Группы посредников маршрутов приложения. * * @var array */ protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, ], 'api' => [ 'throttle:60,1', 'auth:api', ], ];
Группы посредников могут быть назначены на маршруты и действия контроллера с помощью того же синтаксиса, что и для одного посредника. Группы посредников просто делают проще единое назначение нескольких посредников на маршрут:
Route::group(['middleware' => ['web']], function () {
//
});
Не забывайте, группа посредников web автоматически применяется к вашему стандартному файлу routes.php сервис-провайдером RouteServiceProvider.
Параметры посредника
В посредник можно передавать свои дополнительные параметры. Например, если в вашем приложении необходима проверка того, есть ли у аутентифицированного пользователя определённая «роль» для выполнения данного действия, вы можете создать посредника RoleMiddleware, который принимает название роли в качестве дополнительного аргумента.
Дополнительные параметры посредника будут передаваться в посредник после аргумента $next:
<?php
namespace App\Http\Middleware;
use Closure;
class RoleMiddleware
{
/**
* Выполнение фильтрации запроса.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string $role
* @return mixed
*/
public function handle($request, Closure $next, $role)
{
if (! $request->user()->hasRole($role)) {
// Redirect...
}
return $next($request);
}
}
Параметры посредника можно указать при определении маршрута, отделив название посредника от параметров двоеточием :. Сами параметры разделяются запятыми:
Route::put('post/{id}', ['middleware' => 'role:editor', function ($id) {
//
}]);
Посредник terminable
Иногда посредник должен выполнить некоторые действия уже после отправки HTTP-ответа браузеру. Например, посредник «session», поставляемый с Laravel, записывает данные сессии в хранилище после отправки ответа в браузер. Для этого нужно определить посредника как «terminable», добавив в него метод terminate():
<?php
namespace Illuminate\Session\Middleware;
use Closure;
class StartSession
{
public function handle($request, Closure $next)
{
return $next($request);
}
public function terminate($request, $response)
{
// Сохранение данных сессии...
}
}
Метод terminate() получает и запрос, и ответ. Определив посредника как «terminable», вы должны добавить его в список глобальных посредников в вашем HTTP-ядре.
При вызове метода terminate() в посреднике, Laravel получит свежий экземпляр посредника из сервис-контейнера. Если вы хотите использовать тот же самый экземпляр посредника при вызовах методов handle() и terminate(), зарегистрируйте посредника в контейнере при помощи метода singleton().
