- Wprowadzenie
- Definiowanie Middleware
- Rejestrowanie Middleware
- Parametry Middleware
- Terminalny Middleware
Middleware zapewniają wygodny mechanizm do inspekcji i filtrowania żądań HTTP wchodzących do aplikacji. Na przykład Laravel zawiera middleware, który weryfikuje, czy użytkownik aplikacji jest uwierzytelniony. Jeśli użytkownik nie jest uwierzytelniony, middleware przekieruje użytkownika do ekranu logowania aplikacji. Jednak jeśli użytkownik jest uwierzytelniony, middleware pozwoli żądaniu przejść dalej do aplikacji.
Dodatkowe middleware mogą być napisane w celu wykonywania różnych zadań poza uwierzytelnianiem. Na przykład middleware logowania może logować wszystkie przychodzące żądania do aplikacji. Różnorodne middleware są zawarte w Laravel, w tym middleware do uwierzytelniania i ochrony CSRF; jednak wszystkie middleware zdefiniowane przez użytkownika są zazwyczaj zlokalizowane w katalogu app/Http/Middleware aplikacji.
Aby utworzyć nowy middleware, użyj polecenia Artisan make:middleware:
php artisan make:middleware EnsureTokenIsValidTo polecenie umieści nową klasę EnsureTokenIsValid w katalogu app/Http/Middleware aplikacji. W tym middleware pozwolimy na dostęp do trasy tylko wtedy, gdy dostarczony parametr token będzie pasował do określonej wartości. W przeciwnym razie przekierujemy użytkowników z powrotem do URI /home:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureTokenIsValid
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if ($request->input('token') !== 'my-secret-token') {
return redirect('/home');
}
return $next($request);
}
}Jak widać, jeśli dany token nie pasuje do naszego sekretnego tokenu, middleware zwróci przekierowanie HTTP do klienta; w przeciwnym razie żądanie zostanie przekazane dalej do aplikacji. Aby przekazać żądanie głębiej do aplikacji (pozwalając middleware "przejść"), należy wywołać callback $next z $request.
Najlepiej wyobrazić sobie middleware jako serię "warstw", przez które żądania HTTP muszą przejść, zanim trafią do aplikacji. Każda warstwa może zbadać żądanie, a nawet całkowicie je odrzucić.
Note
Wszystkie middleware są rozwiązywane przez kontener usług, więc możesz określić typ dowolnych zależności, których potrzebujesz w konstruktorze middleware.
Oczywiście middleware może wykonywać zadania przed lub po przekazaniu żądania głębiej do aplikacji. Na przykład poniższy middleware wykona pewne zadanie przed obsłużeniem żądania przez aplikację:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class BeforeMiddleware
{
public function handle(Request $request, Closure $next): Response
{
// Perform action
return $next($request);
}
}Jednakże ten middleware wykona swoje zadanie po obsłużeniu żądania przez aplikację:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class AfterMiddleware
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
// Perform action
return $response;
}
}Jeśli chcesz, aby middleware działał podczas każdego żądania HTTP do aplikacji, możesz dodać go do globalnego stosu middleware w pliku bootstrap/app.php aplikacji:
use App\Http\Middleware\EnsureTokenIsValid;
->withMiddleware(function (Middleware $middleware): void {
$middleware->append(EnsureTokenIsValid::class);
})Obiekt $middleware dostarczony do zamknięcia withMiddleware jest instancją Illuminate\Foundation\Configuration\Middleware i jest odpowiedzialny za zarządzanie middleware przypisanym do tras aplikacji. Metoda append dodaje middleware na końcu listy globalnych middleware. Jeśli chcesz dodać middleware na początku listy, powinieneś użyć metody prepend.
Jeśli chcesz ręcznie zarządzać globalnym stosem middleware Laravel, możesz dostarczyć domyślny stos globalnych middleware Laravel do metody use. Następnie możesz dostosować domyślny stos middleware według potrzeb:
->withMiddleware(function (Middleware $middleware): void {
$middleware->use([
\Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks::class,
// \Illuminate\Http\Middleware\TrustHosts::class,
\Illuminate\Http\Middleware\TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class,
\Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Http\Middleware\ValidatePostSize::class,
\Illuminate\Foundation\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
]);
})Jeśli chcesz przypisać middleware do określonych tras, możesz wywołać metodę middleware podczas definiowania trasy:
use App\Http\Middleware\EnsureTokenIsValid;
Route::get('/profile', function () {
// ...
})->middleware(EnsureTokenIsValid::class);Możesz przypisać wiele middleware do trasy, przekazując tablicę nazw middleware do metody middleware:
Route::get('/', function () {
// ...
})->middleware([First::class, Second::class]);Podczas przypisywania middleware do grupy tras, czasami może być konieczne zapobieżenie zastosowaniu middleware do pojedynczej trasy w grupie. Możesz to osiągnąć za pomocą metody withoutMiddleware:
use App\Http\Middleware\EnsureTokenIsValid;
Route::middleware([EnsureTokenIsValid::class])->group(function () {
Route::get('/', function () {
// ...
});
Route::get('/profile', function () {
// ...
})->withoutMiddleware([EnsureTokenIsValid::class]);
});Możesz również wykluczyć dany zestaw middleware z całej grupy definicji tras:
use App\Http\Middleware\EnsureTokenIsValid;
Route::withoutMiddleware([EnsureTokenIsValid::class])->group(function () {
Route::get('/profile', function () {
// ...
});
});Metoda withoutMiddleware może usuwać tylko middleware tras i nie ma zastosowania do globalnych middleware.
Czasami możesz chcieć zgrupować kilka middleware pod jedną nazwą, aby łatwiej było je przypisać do tras. Możesz to osiągnąć za pomocą metody appendToGroup w pliku bootstrap/app.php aplikacji:
use App\Http\Middleware\First;
use App\Http\Middleware\Second;
->withMiddleware(function (Middleware $middleware): void {
$middleware->appendToGroup('group-name', [
First::class,
Second::class,
]);
$middleware->prependToGroup('group-name', [
First::class,
Second::class,
]);
})Grupy middleware mogą być przypisane do tras i akcji kontrolera przy użyciu tej samej składni co pojedyncze middleware:
Route::get('/', function () {
// ...
})->middleware('group-name');
Route::middleware(['group-name'])->group(function () {
// ...
});Laravel zawiera predefiniowane grupy middleware web i api, które zawierają popularne middleware, które możesz chcieć zastosować do swoich tras web i API. Pamiętaj, że Laravel automatycznie stosuje te grupy middleware do odpowiadających plików routes/web.php i routes/api.php:
The web Middleware Group |
|---|
Illuminate\Cookie\Middleware\EncryptCookies |
Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse |
Illuminate\Session\Middleware\StartSession |
Illuminate\View\Middleware\ShareErrorsFromSession |
Illuminate\Foundation\Http\Middleware\ValidateCsrfToken |
Illuminate\Routing\Middleware\SubstituteBindings |
The api Middleware Group |
|---|
Illuminate\Routing\Middleware\SubstituteBindings |
Jeśli chcesz dodać lub poprzedzić middleware do tych grup, możesz użyć metod web i api w pliku bootstrap/app.php aplikacji. Metody web i api są wygodnymi alternatywami dla metody appendToGroup:
use App\Http\Middleware\EnsureTokenIsValid;
use App\Http\Middleware\EnsureUserIsSubscribed;
->withMiddleware(function (Middleware $middleware): void {
$middleware->web(append: [
EnsureUserIsSubscribed::class,
]);
$middleware->api(prepend: [
EnsureTokenIsValid::class,
]);
})Możesz nawet zamienić jeden z domyślnych wpisów grupy middleware Laravel na własny middleware:
use App\Http\Middleware\StartCustomSession;
use Illuminate\Session\Middleware\StartSession;
$middleware->web(replace: [
StartSession::class => StartCustomSession::class,
]);Możesz również całkowicie usunąć middleware:
$middleware->web(remove: [
StartSession::class,
]);Jeśli chcesz ręcznie zarządzać wszystkimi middleware w domyślnych grupach middleware web i api Laravel, możesz całkowicie przedefiniować grupy. Poniższy przykład zdefiniuje grupy middleware web i api z ich domyślnymi middleware, pozwalając na ich dostosowanie według potrzeb:
->withMiddleware(function (Middleware $middleware): void {
$middleware->group('web', [
\Illuminate\Cookie\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
]);
$middleware->group('api', [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
// 'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
]);
})Note
Domyślnie grupy middleware web i api są automatycznie stosowane do odpowiadających plików routes/web.php i routes/api.php aplikacji przez plik bootstrap/app.php.
Możesz przypisać aliasy do middleware w pliku bootstrap/app.php aplikacji. Aliasy middleware pozwalają zdefiniować krótki alias dla danej klasy middleware, co może być szczególnie przydatne dla middleware z długimi nazwami klas:
use App\Http\Middleware\EnsureUserIsSubscribed;
->withMiddleware(function (Middleware $middleware): void {
$middleware->alias([
'subscribed' => EnsureUserIsSubscribed::class
]);
})Po zdefiniowaniu aliasu middleware w pliku bootstrap/app.php aplikacji, możesz użyć aliasu podczas przypisywania middleware do tras:
Route::get('/profile', function () {
// ...
})->middleware('subscribed');Dla wygody, niektóre wbudowane middleware Laravel są domyślnie aliasowane. Na przykład middleware auth jest aliasem dla middleware Illuminate\Auth\Middleware\Authenticate. Poniżej znajduje się lista domyślnych aliasów middleware:
| Alias | Middleware |
|---|---|
auth |
Illuminate\Auth\Middleware\Authenticate |
auth.basic |
Illuminate\Auth\Middleware\AuthenticateWithBasicAuth |
auth.session |
Illuminate\Session\Middleware\AuthenticateSession |
cache.headers |
Illuminate\Http\Middleware\SetCacheHeaders |
can |
Illuminate\Auth\Middleware\Authorize |
guest |
Illuminate\Auth\Middleware\RedirectIfAuthenticated |
password.confirm |
Illuminate\Auth\Middleware\RequirePassword |
precognitive |
Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests |
signed |
Illuminate\Routing\Middleware\ValidateSignature |
subscribed |
\Spark\Http\Middleware\VerifyBillableIsSubscribed |
throttle |
Illuminate\Routing\Middleware\ThrottleRequests or Illuminate\Routing\Middleware\ThrottleRequestsWithRedis |
verified |
Illuminate\Auth\Middleware\EnsureEmailIsVerified |
Rzadko może być potrzebne, aby middleware wykonywał się w określonej kolejności, ale nie masz kontroli nad ich kolejnością, gdy są przypisywane do trasy. W takich sytuacjach możesz określić priorytet middleware za pomocą metody priority w pliku bootstrap/app.php aplikacji:
->withMiddleware(function (Middleware $middleware): void {
$middleware->priority([
\Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
\Illuminate\Cookie\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class,
\Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class,
\Illuminate\Auth\Middleware\Authorize::class,
]);
})Middleware może również otrzymywać dodatkowe parametry. Na przykład, jeśli aplikacja musi zweryfikować, że uwierzytelniony użytkownik posiada daną "rolę" przed wykonaniem określonej akcji, możesz utworzyć middleware EnsureUserHasRole, które otrzymuje nazwę roli jako dodatkowy argument.
Dodatkowe parametry middleware zostaną przekazane do middleware po argumencie $next:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureUserHasRole
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next, string $role): Response
{
if (! $request->user()->hasRole($role)) {
// Redirect...
}
return $next($request);
}
}Parametry middleware mogą być określone podczas definiowania trasy poprzez oddzielenie nazwy middleware i parametrów znakiem ::
use App\Http\Middleware\EnsureUserHasRole;
Route::put('/post/{id}', function (string $id) {
// ...
})->middleware(EnsureUserHasRole::class.':editor');Wiele parametrów może być rozdzielonych przecinkami:
Route::put('/post/{id}', function (string $id) {
// ...
})->middleware(EnsureUserHasRole::class.':editor,publisher');Czasami middleware może potrzebować wykonać pewną pracę po wysłaniu odpowiedzi HTTP do przeglądarki. Jeśli zdefiniujesz metodę terminate w swoim middleware, a twój serwer webowy używa FastCGI, metoda terminate zostanie automatycznie wywołana po wysłaniu odpowiedzi do przeglądarki:
<?php
namespace Illuminate\Session\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class TerminatingMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
return $next($request);
}
/**
* Handle tasks after the response has been sent to the browser.
*/
public function terminate(Request $request, Response $response): void
{
// ...
}
}Metoda terminate powinna otrzymać zarówno żądanie, jak i odpowiedź. Po zdefiniowaniu terminalnego middleware, powinieneś dodać go do listy tras lub globalnych middleware w pliku bootstrap/app.php aplikacji.
Podczas wywoływania metody terminate w middleware, Laravel rozwiąże nową instancję middleware z kontenera usług. Jeśli chcesz użyć tej samej instancji middleware, gdy wywoływane są metody handle i terminate, zarejestruj middleware w kontenerze za pomocą metody singleton kontenera. Zazwyczaj powinno to być wykonane w metodzie register twojego AppServiceProvider:
use App\Http\Middleware\TerminatingMiddleware;
/**
* Register any application services.
*/
public function register(): void
{
$this->app->singleton(TerminatingMiddleware::class);
}