Введение
Посредники (англ. middleware) предоставляют удобный механизм для фильтрации HTTP-запросов вашего приложения. Например, в Laravel есть посредник для проверки аутентификации пользователя. Если пользователь не аутентифицирован, посредник перенаправит его на экран входа в систему. Если же пользователь аутентифицирован, посредник позволит запросу пройти далее в приложение.
Конечно, посредники нужны не только для авторизации. CORS-посредник может пригодиться для добавления особых заголовков ко всем ответам в вашем приложении. А посредник логов может зарегистрировать все входящие запросы.
В Laravel есть несколько стандартных посредников, включая посредники для аутентификации и CSRF-защиты. Все они расположены в каталоге app/Http/Middleware.
Создание посредника
Чтобы создать посредника, используйте команду Artisan shmake:middleware
:
shphp 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, то посредник вернёт клиенту переадресацию, иначе, запрос будет передан далее в приложение. Чтобы передать запрос дальше в приложение (позволяя посреднику «передать» его), просто вызовите замыкание PHP$next
с параметром PHP$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-запроса в вашем приложении, добавьте этот посредник в свойство PHP$middleware
класса app/Http/Kernel.php.
Назначение посредника для маршрутов
Если вы хотите назначить посредника для конкретных маршрутов, то сначала вам надо добавить ключ посредника в класс app/Http/Kernel.php. По умолчанию свойство PHP$routeMiddleware
этого класса содержит записи посредников Laravel. Чтобы добавить ваш собственный посредник, просто добавьте его к этому списку и присвойте ему ключ на свой выбор. Например:
// в классе App\Http\Kernel...
protected $routeMiddleware = [
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
//для версии 5.2 и выше:
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
//для версии 5.3 и выше:
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
//для версии 5.2 и ранее:
'auth' => \App\Http\Middleware\Authenticate::class,
];
добавлено в 5.3 ()
Когда посредник определён в HTTP-ядре, вы можете использовать метод middleware для назначения посредника на маршрут:
Route::get('admin/profile', function () {
//
})->middleware('auth');
Для назначения нескольких посредников для маршрута:
Route::get('/', function () {
//
})->middleware('first', 'second');
добавлено в 5.2 () 5.1 () 5.0 ()
Когда посредник определён в HTTP-ядре, вы можете использовать ключ middleware в массиве параметров маршрута:
Route::get('admin/profile', ['middleware' => 'auth', function () {
//
}]);
Используйте массив для назначения нескольких посредников для маршрута:
Route::get('/', ['middleware' => ['first', 'second'], function () {
//
}]);
Вместо использования массива вы можете использовать сцепку метода PHPmiddleware()
с определением маршрута:
Route::get('/', function () {
//
})->middleware(['first', 'second']);
При назначении посредника вы можете указать полное имя класса:
use App\Http\Middleware\CheckAge;
Route::get('admin/profile', function () {
//
})->middleware(CheckAge::class);
Группы посредников
Иногда бывает полезно объединить несколько посредников под одним ключом, чтобы проще назначать их на маршруты. Это можно сделать при помощи свойства PHP$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, который принимает название роли в качестве дополнительного аргумента.
Дополнительные параметры посредника будут передаваться в посредник после аргумента PHP$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);
}
}
Параметры посредника можно указать при определении маршрута, отделив название посредника от параметров двоеточием :. Сами параметры разделяются запятыми:
добавлено в 5.3 ()
Route::put('post/{id}', function ($id) {
//
})->middleware('role:editor');
добавлено в 5.2 () 5.1 () 5.0 ()
Route::put('post/{id}', ['middleware' => 'role:editor', function ($id) {
//
}]);
Посредник terminable
Иногда посредник должен выполнить некоторые действия уже после отправки HTTP-отклика браузеру. Например, посредник «session», поставляемый с Laravel, записывает данные сессии в хранилище после отправки ответа в браузер. Если вы определите метод PHPterminate()
в посреднике, то он будет автоматически вызываться после отправки отклика в браузер:
<?php
namespace Illuminate\Session\Middleware;
use Closure;
class StartSession
{
public function handle($request, Closure $next)
{
return $next($request);
}
public function terminate($request, $response)
{
// Сохранение данных сессии...
}
}
Метод PHPterminate()
получает и запрос, и ответ. Определив посредника как «terminable», вы должны добавить его в список глобальных посредников в вашем HTTP-ядре.
При вызове метода PHPterminate()
в посреднике, Laravel получит свежий экземпляр посредника из сервис-контейнера. Если вы хотите использовать тот же самый экземпляр посредника при вызовах методов PHPhandle()
и PHPterminate()
, зарегистрируйте посредника в контейнере при помощи метода PHPsingleton()
.
Комментарии (5)
Не совсем понятно про посредников «до» и «после». У них имена просто должны начинаться с «Before» и «After» соответственно ?
Внимательно посмотрите на тело метода, в одном сначала идет выполнение кода, а потом передаем дальше, а во втором сначала передаем дальше, а потом выполняем код. http://joxi.ru/xAee68Wt57Q6Ay - на скрине, думаю, будет чуть понятнее
Во-втором случае, правильнее было бы написать не «сначала передаем дальше, а потом выполняем код», а «сначала обрабатывается запрос, потом выполняется код посредника», т.к. запрос передается дальше в обоих случаях в конце с помощью return.
Действительно сбивает заголовок «Посредник «до» и «после»» а после него
PHPclass BeforeMiddleware
...
PHPclass AfterMiddleware
...
У меня возникло недопонимание которое решилось после прочтения комментариев.
Может быть лучше переименовать заголовок «Посредник «до» и «после»» в «момент срабатывания посредника», а фразу «Момент, в который сработает посредник — до или после запроса, зависит от него самого.»
на «Момент, в который сработает посредник указывается в
PHPfunction handle
. И если нам необходимо выполнить действия до запроса, то необходимо их выполнить передPHP$response = $next($request);
»Советую глянуть на годный урок https://youtu.be/c32ihcUI-No