Посредники
11.x
.
Почему это важно?
Введение
Посредники предоставляют удобный механизм для фильтрации HTTP-запросов вашего приложения. Например, в Laravel есть посредник для проверки аутентификации пользователя. Если пользователь не аутентифицирован, посредник перенаправит его на страницу входа в систему. Если же пользователь аутентифицирован, посредник позволит запросу пройти далее в приложение.
Конечно, посредники нужны не только для авторизации. CORS-посредник может пригодиться для добавления особых заголовков ко всем ответам в вашем приложении. А посредник логов может зарегистрировать все входящие запросы.
В Laravel есть несколько стандартных посредников, включая посредники для аутентификации и CSRF-защиты. Все они расположены в директории app/Http/Middleware
.
Создание посредника
Чтобы создать посредника, используйте команду Artisan make:middleware
:
php artisan make:middleware CheckAge
Эта команда поместит новый класс CheckAge
в вашу директорию app/Http/Middleware
. В этом посреднике мы будем пропускать только те запросы, в которых age будет больше 200, а во всех остальных случаях будем перенаправлять пользователей на home
URI.
<?php
namespace App\Http\Middleware;
use Closure;
class CheckAge
{
/**
* Обработка входящего запроса.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($request->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' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];
Когда посредник определён в HTTP-ядре, вы можете использовать метод middleware
для назначения посредника роуту:
Route::get('admin/profile', function () {
//
})->middleware('auth');
Также можно назначить несколько посредников роуту:
Route::get('/', function () {
//
})->middleware('first', 'second');
При назначении посредника вы можете указать полное имя класса:
use App\Http\Middleware\CheckAge;
Route::get('admin/profile', function () {
//
})->middleware(CheckAge::class);
Группы посредников
Иногда бывает полезно объединить несколько посредников под одним ключом, чтобы проще назначать их на маршруты. Это можно сделать при помощи свойства $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,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:60,1',
'auth:api',
],
];
Группы посредников могут быть назначены роутам и действия контроллера с помощью того же синтаксиса, что и для одного посредника. Группы посредников просто делают проще единое назначение нескольких посредников на роут:
Route::get('/', function () {
//
})->middleware('web');
Route::group(['middleware' => ['web']], function () {
//
});
Изначально группа посредников
web
автоматически применяется к вашему файлуroutes/web.php
сервис-провайдеромRouteServiceProvider
.
Параметры посредника
В посредник можно передавать дополнительные параметры. Например, если в вашем приложении необходима проверка того, есть ли у аутентифицированного пользователя определённая “роль” для выполнения данного действия, вы можете создать посредника CheckRole
, который принимает название роли в качестве дополнительного аргумента.
Дополнительные параметры посредника будут передаваться в посредник после аргумента $next
:
<?php
namespace App\Http\Middleware;
use Closure;
class CheckRole
{
/**
* Обработка входящего запроса.
*
* @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}', function ($id) {
//
})->middleware('role:editor');
Посредник terminable
Иногда посредник должен выполнить некоторые действия уже после отправки HTTP-ответа браузеру. Например, посредник “session”, поставляемый с Laravel, записывает данные сессии в хранилище после отправки ответа в браузер. Если вы определите метод terminate
в посреднике, то он будет автоматически вызываться после отправки ответа в браузер.
<?php
namespace Illuminate\Session\Middleware;
use Closure;
class StartSession
{
public function handle($request, Closure $next)
{
return $next($request);
}
public function terminate($request, $response)
{
// Store the session data...
}
}
Метод terminate
должен получать и запрос и ответ. Определив terminable-посредника, вы должны добавить его в список посредников роута или глобальных посредников в файл app/Http/Kernel.php
.
При вызове метода terminate
в посреднике, Laravel получит свежий экземпляр посредника из сервис-контейнера. Если вы хотите использовать тот же самый экземпляр посредника при вызовах методов handle
и terminate
, зарегистрируйте посредника в контейнере при помощи метода singleton
.