laravel5.5 - Middleware

Keywords: PHP Laravel REST

As a request and response filter, Middleware in laravel has two main functions.

1. Intercept and filter requests before they reach the controller layer. Only validated requests can reach the controller layer.

2. Or filter the data or page response that has been computed in the controller before it returns, and the response that has been validated can be returned to the client.

Middleware is usually created by artisan command

php artisan make:middleware middleware name

The middleware created on the command line is stored in the / app/Http/Middleware folder

These middleware need to be added manually to the configuration of the / app/Http/Kernel.php file, which inherits the kernel core. When the application entry initializes the kernel core, the configuration in the vendor\laravelframeworksrc\Illuminate\Foundation\Http\Kernel.php file covers the relevant configuration in the core, which of course includes middleware middleware. We can add a print statement on the first line of the kernel class constructor, print $this - > middleware, $this - > middleware Groups, and then we will see the middleware previously written in the / APP / Http / Kernel. PHP file.

But not only here, but also when the routing is loaded, the middleware in the default web array of the system will be loaded once and filtered when the response object is returned, which will be seen later.

Next, take a look at where middleware started filtering. In the first blog post on entry files, we learned that laravel started with the handle method in the kernel core.

    public function handle($request)
    {
        try {
            //Enable http Method Coverage Parameters
            $request->enableHttpMethodParameterOverride();
            //Send requests through routing
            $response = $this->sendRequestThroughRouter($request);
        } catch (Exception $e) {
            $this->reportException($e);

            $response = $this->renderException($request, $e);
        } catch (Throwable $e) {
            $this->reportException($e = new FatalThrowableError($e));

            $response = $this->renderException($request, $e);
        }

        $this->app['events']->dispatch(
            new Events\RequestHandled($request, $response)
        );

        return $response;
    }

    protected function sendRequestThroughRouter($request)
    {
        //Store requests in containers
        $this->app->instance('request', $request);
        //Eliminate facade Facade
        Facade::clearResolvedInstance('request');
        //Initialization boot
        $this->bootstrap();
        //Enter requests into Middleware
        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }

 The final return statement in the sendRequestThroughRouter method of the kernel core begins to use middleware to filter the requests received by the system.

In the last blog post, we mentioned the decoration mode and the pipeline mode based on laravel, which is used here.

Jumping all the way from the Pipeline object, we found that the base class of this object is laravel vendor laravel framework src Illuminate Pipeline Pipeline. php. The construction method of pipeline object introduces the container of laravel system into it. The principle of pipeline mode is no longer discussed here, but the key method is then.

 1     public function then(Closure $destination)
 2     {
 3         //array_reduce — Iteratively simplify an array to a single value with a callback function
 4         $pipeline = array_reduce(
 5             //array_reverse Sort arrays backwards. $this->carry()Returns a closure function for iteration
 6             array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination)
 7         );
 8 
 9         return $pipeline($this->passable);
10     }
11 
12     protected function carry()
13     {
14         return function ($stack, $pipe) {
15             return function ($passable) use ($stack, $pipe) {
16                 if (is_callable($pipe)) {
17                     // If the pipe is an instance of a Closure, we will just call it directly but
18                     // otherwise we'll resolve the pipes out of the container and call it with
19                     // the appropriate method and arguments, returning the results back out.
20                     return $pipe($passable, $stack);
21                 } elseif (! is_object($pipe)) {
22                     //Resolution string
23                     list($name, $parameters) = $this->parsePipeString($pipe);
24 
25                     // If the pipe is a string we will parse the string and resolve the class out
26                     // of the dependency injection container. We can then build a callable and
27                     // execute the pipe function giving in the parameters that are required.
28                     //Remove the corresponding object from the container
29                     $pipe = $this->getContainer()->make($name);
30                     $parameters = array_merge([$passable, $stack], $parameters);
31                 } else {
32                     // If the pipe is already an object we'll just make a callable and pass it to
33                     // the pipe as-is. There is no need to do any extra parsing and formatting
34                     // since the object we're given was already a fully instantiated object.
35                     $parameters = [$passable, $stack];
36                 }
37                 dd($parameters);
38                 //Judging the existence of middleware objects handle Method, and will just $request Object and $stack Closures are passed into the middleware as parameters handle Method
39                 return method_exists($pipe, $this->method)
40                                 ? $pipe->{$this->method}(...$parameters)
41                                 : $pipe(...$parameters);
42             };
43         };
44     }

As you can see, the other methods in the pipeline class are very simple assignment methods. In the then method, we also wrote about the array_reduce function we used in the last understanding of the pipeline pattern, which is much more cordial to see. Array_reverse ($this - > pipes) is the reverse of the middleware array, because the closure in pipeline mode runs from the outside to the inside like an onion. The method of $this - > prepareDestination ($destination) is introduced by the former then method. After passing through the middleware, the business code that needs to be executed will start calling the controller.

The key code in $this - > carry () is that this method only looks at the outermost layer and returns a closure function, which is equivalent to writing the closure directly in array_reduce. The closure function inside is a familiar pipeline closure method. The two parameters of use represent the execution result of this closure and one of the parameters in the middleware array. The $passable variable represents the $request object.

In this closure, several if judgments are used to determine the value type of $pipe, which is the type in the value of our middleware array. If it is a closure, it runs directly, and if it is not an object, it loads the object from the container. If none of the rest is, it represents the type as the object, builds the parameters directly, and starts calling. Remember that our custom middleware code is written in the handle method, where $passable and $stack are the variables $request and losure $next passed into the handle at run time. And what is the next method here?

Let's open the pipeline file vendor laravel framework src Illuminate Routing Pipeline. PHP in the routing

 1     protected function carry()
 2     {
 3         return function ($stack, $pipe) {
 4             return function ($passable) use ($stack, $pipe) {
 5                 try {
 6                     //Start the just pipeline closure with this function
 7                     $slice = parent::carry();
 8                     //$stack, $pipe Still the closure result and the value in the middleware array
 9                     $callable = $slice($stack, $pipe);
10                     //$passable still request object
11                     return $callable($passable);
12                 } catch (Exception $e) {
13                     return $this->handleException($passable, $e);
14                 } catch (Throwable $e) {
15                     return $this->handleException($passable, new FatalThrowableError($e));
16                 }
17             };
18         };
19     }

When the pipeline closure is executed, the middleware is temporarily terminated. The $this - > dispatchToRouter () method passed in by the kernel core before execution started.

 Now let's look at how routing arrives at the controller after passing through the middleware. After a series of jumps, the dispatchToRouter method arrives at the dispatchToRoute method of the Router class. The findRoute method obtains the set of Routes objects (this object contains all the routing objects, which were introduced in the previous chapter on routing) and obtains the current routing from the request object (this search process uses the Collection base object, because it is not the focus of this article, it should be treated as a black box method for the time being).

    public function dispatchToRoute(Request $request)
    {
        return $this->runRoute($request, $this->findRoute($request));
    }

    //vendor\laravel\framework\src\Illuminate\Routing\Router.php
    protected function runRoute(Request $request, Route $route)
    {
        //Setting the closure function of the return route request In object
        $request->setRouteResolver(function () use ($route) {
            return $route;
        });
        //Binding event listener
        $this->events->dispatch(new Events\RouteMatched($route, $request));

        return $this->prepareResponse($request,
            $this->runRouteWithinStack($route, $request)
        );
    }

    protected function runRouteWithinStack(Route $route, Request $request)
    {
        //Whether to disable Middleware
        $shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
                                $this->container->make('middleware.disable') === true;
        //Getting System Routing Middleware,
        $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route);
        //The same middleware invocation as before
        return (new Pipeline($this->container))
                        ->send($request)
                        ->through($middleware)
                        ->then(function ($request) use ($route) {
                            //Wrap the execution results into response Response object
                            return $this->prepareResponse(
                                //Access Controller
                                $request, $route->run()
                            );
                        });
    }

    //vendor\laravel\framework\src\Illuminate\Routing\Route.php    
    public function run()
    {
        $this->container = $this->container ?: new Container;

        try {
            //Execution in the form of a controller
            if ($this->isControllerAction()) {
                return $this->runController();
            }
            //Execute in closure mode
            return $this->runCallable();
        } catch (HttpResponseException $e) {
            return $e->getResponse();
        }
    }

At the beginning, we mentioned that there will be a middleware filter when routing loads. That's it. When the code is executed into the runRoute method, middleware filtering is done again, this time the middleware is the system middleware written in the web array. And this time through the middleware, the controller will be executed directly, or the code written in the routing closure. The returned results are packaged as response objects and returned to the browser in turn.

The controller method is started by the controller Dispatcher class, which is located in vendor laravel framework src Illuminate Routing Controller Dispatcher. php. Its dispatch method receives three parameters: routing, controller and method. The printing results are as follows

There will be a default middleware array in the controller, because middleware can also be defined in the controller, but we usually define it directly in the kernel.

 Let's look at what this code actually does.

 1     public function dispatch(Route $route, $controller, $method)
 2     {
 3         //Analytic dependency
 4         $parameters = $this->resolveClassMethodDependencies(
 5             //Get the parameters written in the routing
 6             $route->parametersWithoutNulls(), $controller, $method
 7         );
 8         //Judge if there is callAction Method. In fact controller Executed in the parent class of call_user_func_array
 9         if (method_exists($controller, 'callAction')) {
10             return $controller->callAction($method, $parameters);
11         }
12         //If not, call back as a normal class
13         return $controller->{$method}(...array_values($parameters));
14     }

Oh, I almost forgot that the instantiation of the controller was performed in the runController method of the Route class.

 1     public function getController()
 2     {
 3         if (! $this->controller) {
 4             //Getting the controller string from the routing
 5             $class = $this->parseControllerCallback()[0];
 6             //Segmentation by strings make Example of Controller
 7             $this->controller = $this->container->make(ltrim($class, '\\'));
 8         }
 9 
10         return $this->controller;
11     }
12 
13     protected function parseControllerCallback()
14     {
15         //laravel Encapsulated string manipulation class, route Of action It records what we wrote in route Controller-related strings in files
16         return Str::parseCallback($this->action['uses']);
17     }

So, here we combed the process from middleware to controller again. This chapter on middleware is about that.

Posted by ForgotMyPass on Tue, 26 Feb 2019 02:27:22 -0800