# Middleware

Middleware provide a convenient mechanism for inspecting and modifying HTTP requests before sending.

Additional middleware can be written to perform a variety of tasks. For example, a logging middleware might log all outgoing requests and responses.

# Defining Middleware

To create a new middleware, create a new Invokable class and put your logic inside __invoke method:

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

final class AttachContentTypeRequestHeader
{
    private $contentType;

    public function __construct(string $contentType)
    {
        $this->contentType = $contentType;
    }

    public function __invoke(RequestInterface $request, callable $next): ResponseInterface
    {
        return $next($request->withHeader('Content-Type', $this->contentType));
    }
}

In this middleware, we will add a content type header to the request depends on the body type.

Middleware also can be a Closure:

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

function attach_content_type(string $contentType): Closure {
    return static function (RequestInterface $request, callable $next): ResponseInterface {
        return $next($request->withHeader('Content-Type', $contentType));
    };
}

# Middleware & Responses

Of course, a middleware can perform tasks before or after sending the request. For example, the following middleware would perform some task before the request is sent by the client:

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

final class BeforeMiddleware
{
    public function __invoke(RequestInterface $request, callable $next): ResponseInterface
    {
        // Perform action

        return $next($request);
    }
}

However, this middleware would perform its task after the request is sent:

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

final class AfterMiddleware
{
    public function __invoke(RequestInterface $request, callable $next): ResponseInterface
    {
        $response = $next($request);

        // Perform action

        return $response;
    }
}

# Registering Middleware

# Default Middleware

To register default middleware, list the middleware class in the defaultMiddleware method of your connector class.

use Fansipan\Contracts\ConnectorInterface;
use Fansipan\Traits\ConnectorTrait;

final class Connector implements ConnectorInterface
{
    use ConnectorTrait;

    protected function defaultMiddleware(): array
    {
        return [
            new Middleware\AddCustomHeader(),
        ];
    }
}

# Adding & Removing Middleware

Once the middleware has been created, you may use the push method to assign middleware to the pipeline:

$connector->middleware()->push(new AddHeader('X-Foo', 'baz'));

Adding middleware to the connector will apply to all requests. If you only want to apply it to the current request, use the middleware method of ConnectorConfigurator to create a cloned instance of your connector:

use Fansipan\ConnectorConfigurator;

$response = (new ConnectorConfigurator())
    ->middleware(new AddHeader('X-Foo', 'baz'))
    ->configure($connector)
    ->send(new MyRequest());

Creating a middleware that modifies a request is made much simpler using the Fansipan\Middleware\Interceptor::request() method. This middleware accepts a function that takes the request argument:

use Fansipan\Middleware\Interceptor;
use Psr\Http\Message\RequestInterface;

$connector->middleware()->push(Interceptor::request(static function (RequestInterface $request) {
    return $request->withHeader('X-Foo', 'bar');
}));

Modifying a response is also much simpler using the Fansipan\Middleware\Interceptor::response() middleware:

use Fansipan\Middleware\Interceptor;
use Psr\Http\Message\ResponseInterface;

$connector->middleware()->push(Interceptor::response(static function (ResponseInterface $response) {
    return $response->withHeader('X-Foo', 'bar');
}));

You can give middleware a name, which allows you to add middleware before other named middleware, after other named middleware, or remove middleware by name.

// Add a middleware with a name
$connector->middleware()->push(Interceptor::request(static function (RequestInterface $request) {
    return $request->withHeader('X-Foo', 'bar');
}), 'add_foo');

// Add a middleware before a named middleware (unshift before).
$connector->middleware()->before('add_foo', Interceptor::request(static function (RequestInterface $request) {
    return $request->withHeader('X-Baz', 'qux');
}), 'add_baz');

// Add a middleware after a named middleware (pushed after).
$connector->middleware()->before('add_baz', Interceptor::request(static function (RequestInterface $request) {
    return $request->withHeader('X-Lorem', 'Ipsum');
}));

// Remove a middleware by name
$connector->middleware()->remove('add_foo');