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
()
.