What is the purpose of Middleware in relation to Apis and Web Applications made in Slim?

Asked

Viewed 3,432 times

15

I’m reading regarding Middleware no framework Slim, however, I could not understand the purpose of Middleware in relation to Rest Apis or Web Applications built in Slim.

I was also talking to Guilherme Nascimento in the chat regarding this subject matter and he told me this:

middleware controls all HTTP, let’s say middleware is a vc handler can use or let scroll, slim authentication enters in the middle of slim control HTTP and handles the situation

This conversation has generated me some more doubts, which I will address right below.

Doubts

  1. What is the purpose of middleware in Slim Framework, in building Apis or Web Applications?
  2. What is the relationship that middleware has with the HTTP protocol? And in what sense it controls the whole HTTP?

If you’re going to give code examples, I’d like it to be in PHP.

1 answer

24


William’s words sum up the concept of middleware. The middleware is a structure to work directly on the HTTP protocol, having as input the HTTP request received by the server and as output the generated HTTP response. This response may or may not be generated by middleware. In the case of authentication, for example, middleware responsible will verify the credentials present in the request; if null or invalid, the itself middleware will generate the access response denied; but otherwise, it allows the request to propagate by the application.

Too Long, Read Anyway

The answer will be long, but worth reading (worth to me at least).

Middleware and the HTTP protocol

The middleware works directly on the HTTP protocol - in fact, any WEB application that uses such a protocol will work directly on it, but is not always as frank as cosstuma is with the middleware. In PHP, this is usually very clear because the middleware work on the recommendation set out in PSR-7: HTTP Message Interfaces. This recommendation defines an interface for HTTP requests:

Psr\Http\Message\RequestInterface

Or, precisely to represent the request received by the server, the interface (this interface extends to the previous one):

Psr\Http\Message\ServerRequestInterface

To work with HTTP responses, the interface has been defined:

Psr\Http\Message\ResponseInterface

These three can be considered the trivial ones of the PSR-7. The implementations of these interfaces can be found in the repository PSR official and installed in the project through Composer:

composer install psr/http-message

In particular, the framework Slim uses these interfaces. To confirm, just check the implementations of the classes Request and Response:

Request.php:

use Psr\Http\Message\ServerRequestInterface;

class Request extends Message implements ServerRequestInterface {}

Response.php:

use Psr\Http\Message\ResponseInterface;

class Response extends Message implements ResponseInterface {}

So from this moment on, when referring to an object Request is understood by an object that implements the interface Psr\Http\Message\RequestInterface (or equivalent), whether Slim or not, and when referring to an object Response is understood by an object that implements the interface Psr\Http\Message\ResponseInterface (or equivalent), whether Slim or not.

Implementation of a middleware

This way, then, it becomes clearer which are the inputs and outputs of a middleware: an object Request incoming ($request) and an object Response outgoing ($response). There is a new PSR that deals only on middleware: to PSR-10, but this recommendation is still at the draft stage and may change, although it already has a well consolidated and applied structure in some frameworks.

Simply put, there are 3 different ways to implement a middleware:

1. Double Pass

This approach is currently the most widely used and was based on framework Express:

function(request, response, next): response

In this approach, we can define that the middleware is defined as an object callable (commonly as Closure), which takes three parameters:

  • request: an object Request;
  • response: an object Response;
  • next: an object callable representing the next middleware;

This approach makes more sense when the middleware are put in the form of pipeline, that is, aligned, in which the output of one will serve as the input of the other, explaining the reason of the object Response also be propagated as a parameter.

inserir a descrição da imagem aqui

Figure 1: Block diagram representing the organization of middleware aligned.

An example of this implementation would be:

function auth ($request, $response, $next)
{
    if (! Auth::validate()) {
        $response = new Response(401, "Unauthorized");
    }

    return $next($request, $response);
}

Note that in the middleware authentication logic is executed to verify credentials and, if it fails, the answer is changed to a 401 response. The problem with this structure is that all middleware will be executed and those who need authentication to be executed will have to make the verification of which is the answer that arrived as parameter:

function anything ($request, $response, $next)
{
    if ($response->getCode() == 200) {
        // Faça algo apenas se a resposta está OK.
    }

    return $next($request, $response);
}

To get around this problem, some applications prefer the battery structure to aligned, as the stack structure allows what we call short-circuiting in the HTTP response. For example, if the middleware Authentication already generate a 401 response, there is no need to run the rest of the application, so this response is "short-circuited" for the output of the application. To do this, just slightly modify the code:

function auth ($request, $response, $next)
{
    if (! Auth::validate()) {
        return new Response(401, "Unauthorized");
    }

    return $next($request, $response);
}

Note that instead of just modifying the object $response, the new answer is returned, without having to execute the next middleware, through $next. In this way, the graphic representation stays this way:

inserir a descrição da imagem aqui

Figure 2: Block diagram representing the organization of middleware piled.

Realize that the object Response that is sent out is the return of the first middleware, no more than the latter, which is what allows the short-circuit of the response.

Projects using this approach:

2. Single Pass

The approach single pass is very similar to the double pass, differing only that the object Response is not passed as parameter:

function(request, next): response

In this approach, we can define that the middleware is defined as an object callable (commonly as Closure), which takes two parameters:

  • request: an object Request;
  • next: an object callable representing the next middleware;

In this way, each middleware does not have access to the HTTP response until one is generated by the application, at middleware more internal. That is, in this approach, only the cell structure is possible (Figure 2). The short-circuit response technique can also be applied in this approach.

function auth ($request, $next)
{
    if(! Auth::validate())
    {
        return new Response(401, "Unauthorized");
    }

    return $next($request);
}

Projects using single pass:

Personal note: I personally think it makes much more sense to approach single pass than the double pass, when using the middleware together with an MVC application, the controller is expected to generate the final response and being the middleware executed before the controller, there will be no input response.

3. Middleware Interface

PSR-10 defined a third approach based on an interface to middleware. This interface defines only one method, called process, with two parameters: the object Request and an object Delegate, that represents the next middleware. As such PSR is still in draft stage, there is no implementation officer, then I’ll set mine as an example:

interface Middleware
{
    /**
     * Processa uma requisição HTTP de entrada e retorna uma resposta.
     * 
     * O middleware é responsável por fazer o tratamento da requisição HTTP de
     * entrada e retornar uma resposta. A resposta pode ser gerada pelo
     * middleware ou o mesmo pode delegar o retorno ao próximo middleware da
     * lista, através do objeto `$delegate`. Para o primeiro caso, quando o
     * middleware gera a resposta retornada, todos os middlewares restantes na
     * lista serão ignorados pela aplicação.
     *
     * @param Request $request Requisição a ser tratada pelo middleware. 
     * @param Delegate $delegate Instância de Delegate que controla os middlewares.
     * @return Response Resposta gerada pelo middleware.
     */
    public function process($request, $delegate);
}

Some comments on this approach:

Why not use the __invoke method?

In the previous approaches, double pass and single pass, how the middleware are defined as closures, they can be implemented through classes using the method __invoke, then the approach defined in the PSR-10 chose not to use it to not generate conflicts and maintain the forwards Compatibility.

Why use the name process for the method?

Some other existing structures, mainly related to the MVC architecture, already have similar names, such as: __invoke, as mentioned above, handle, as used in HttpKernel of Symfony, and dispatch, as used in DispatchableInterface of Zend. In order not to create conflict or confusion with these structures, the name was chosen process.

More details can be found directly on the PSR-10, but for simplicity, the object Delegate must have at least the process, which is analogous to the functioning of $next in the previous approaches. An example of middleware in this approach would be:

class AuthMiddleware implements MiddlewareInterface
{
    public function process($request, $delegate)
    {
        if (! Auth::validate()) {
            return new Response(401, "Unauthorized");
        }

        return $delegate->process($request);
    }
}

The graphical representation of this approach is the same as the single pass, illustrated in Figure 2, therefore, the short circuit technique of the response is also allowed, as shown in the above code.

Middleware and MVC applications

The vast majority of frameworks using the concept of middleware have used MVC architecture before, so today they unite the two concepts, defining the middleware as plug-in components to the application - that is, you can add or remove without having to modify the logic of your application itself. Slim is an example of this: it uses the approach double pass stack-shaped and centralize the MVC application within it, as in the figure below:

inserir a descrição da imagem aqui

Figure 3: Illustration of how the interaction between middleware and MVC applications in the vast majority of cases.

Superficially, we can understand the MVC application as a great middleware, because the entry of it is an HTTP request and it generates an appropriate response. The concept of middleware, in fact, it serves to delegate distinct tasks to distinct structures. For example, the MVC architecture can create a context to work with very diverse routes, running certain controllers for each existing route, but if implementing authentication rules, cache control, etc, controller logic begins to complicate in exponential form. With the concept of middleware you can delegate the MVC application to do what it does best and delegate the authentication and cache control logics to frameworks adapted to this: the middleware.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.