Skip to content
Permalink
master
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time

Middleware

Yii works with HTTP using abstraction layer built around PSR-7 HTTP message interfaces and PSR-15 request handler/middleware interfaces.

The application is composed of one or several middleware. When the URL is requested, the request object is passed to the middleware dispatcher that starts executing middleware. Each middleware, given the request, can:

  • Pass request to the next middleware or return a response.
  • Perform some work before and after next middleware.

Depending on middleware used, application behavior may vary significantly.

Middleware

In the above each next middleware wraps the previous middleware. Alternatively, it could be presented as follows:

Middleware

Using middleware

Any PSR-15 compatible middleware could be used with Yii and there are many. Say, we need to add basic authentication one of the application URLs. URL-dependent middleware are configured using router, so we need to modify router factory.

Authentication middleware is implemented by middlewares/http-authentication package so execute composer require middlewares/http-authentication in the application root directory.

Now register the middleware in DI container configuration config/web.php:

<?php
\Middlewares\BasicAuthentication::class => [
    'class' => \Middlewares\BasicAuthentication::class,
    '__construct()' => [
        'users' => [
            'foo' => 'bar',
        ],
    ],
    'realm()' => ['Access to the staging site via basic auth'],
    'attribute()' => ['username'],
],

In the config/routes.php, add new route:

<?php

declare(strict_types=1);

use Yiisoft\Router\Route;
use App\Controller\SiteController;
use Middlewares\BasicAuthentication;


return [
    //...
    Route::get('/basic-auth')->([SiteController::class, 'auth'])
        ->name('site/auth')
        ->addMiddleware(BasicAuthentication::class)
];

In the above when configuring routing, we are binding /basic-auth URL to a chain of middleware consisting of basic authentication, and the action itself. A chain is a special middleware that executes all the middleware it is configured with.

The action itself may be the following:

public function auth(ServerRequestInterface $request): ResponseInterface
{
    $response = $this->responseFactory->createResponse();
    $response->getBody()->write('Hi ' . $request->getAttribute('username'));
    return $response;
}

Basic authentication middleware wrote to request username attribute, so we can access the data if needed.

To apply middleware to application overall regardless of URL, adjust config/application.php:

return [
    Yiisoft\Yii\Http\Application::class => [
        '__construct()' => [
            'dispatcher' => DynamicReference::to(static function (Injector $injector) {
                return ($injector->make(MiddlewareDispatcher::class))
                    ->withMiddlewares(
                        [
                            ErrorCatcher::class,
                            BasicAuthentication::class,
                            SessionMiddleware::class,
                            CsrfMiddleware::class,
                            Router::class,
                        ]
                    );
            }),
            'fallbackHandler' => Reference::to(NotFoundHandler::class),
        ],
    ],
];

Creating your own middleware

To create a middleware you need to implement a single process method of Psr\Http\Server\MiddlewareInterface:

public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface;

There are multiple ways to handle request and choosing one depends on what the middleware should achieve.

Forming response directly

To respond directly one needs a response factory passed via constructor:

<?php
declare(strict_types=1);

use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class RespondingMiddleware implements MiddlewareInterface
{
    private ResponseFactoryInterface $responseFactory;

    public function __construct(ResponseFactoryInterface $responseFactory)
    {
        $this->responseFactory = $responseFactory;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface
    {
        $response = $this->responseFactory->createResponse();
        $response->getBody()->write('Hello!');
        return $response;
    }
}

Delegating handling to the next middleware

If middleware either is not intended form response / modify request or cannot do it this time, handling could be left to next middleware in the stack:

return $next->handle($request);

In case you need to pass data to the next middleware, you can use request attributes:

$request = $request->withAttribute('answer', 42);
return $next->handle();

To get it in the next middleware:

$answer = $request->getAttribute('answer');

Capturing response to manipulate it

You may want to capture response to manipulate it. It could be useful for adding CORS headers, gzipping content etc.

$response = $next->handle($request);
// extra handing
return $response;