Detaillierte Erläuterung der Laravel-Routing-Route

小云云
Freigeben: 2023-03-20 11:32:02
Original
19448 Leute haben es durchsucht

Routing ist der Weg für die Außenwelt, auf Laravel-Anwendungen zuzugreifen, oder Routing definiert die spezifische Art und Weise, wie Laravel-Anwendungen Dienste für die Außenwelt bereitstellen: Nur über den angegebenen URI, die HTTP-Anforderungsmethode und die Routing-Parameter (optional). Auf die Routing-Definition muss korrekt zugegriffen werden. Unabhängig davon, ob der dem URI entsprechende Handler ein einfacher Verschluss ist oder die Controller-Methode keine entsprechende Route hat, kann die Außenwelt nicht darauf zugreifen. Heute werfen wir einen Blick darauf, wie Laravel Routing entwirft und implementiert.

Wir definieren die Route normalerweise wie folgt in der Routing-Datei:

Route::get('/user', 'UsersController@index');
Nach dem Login kopieren

Aus der obigen Route können wir erkennen, dass der Client den URI mithilfe der HTTP-GET-Methode „/user“ anfordert. Laravel leitet die Anfrage schließlich zur Verarbeitung an die Indexmethode der UsersController-Klasse weiter und gibt dann die Antwort in der Indexmethode an den Client zurück.

Die Route-Klasse, die oben bei der Registrierung von Routen verwendet wird, heißt Facade in Laravel. Sie bietet eine einfache Möglichkeit, auf den an den Service-Container gebundenen Service-Router zuzugreifen. Ich habe vor, einen separaten Blog zu schreiben Hier müssen wir nur wissen, dass die aufgerufenen statischen Methoden der Route-Fassade den Methoden des Router-Dienstes im Service-Container entsprechen, sodass Sie sich die obige Route auch als Registrierung vorstellen können dies:

app()->make('router')->get('user', 'UsersController@index');
Nach dem Login kopieren

Der Router-Dienst wird an den Service-Container gebunden, indem der RoutingServiceProvider beim Instanziieren der Anwendung im Konstruktor registriert wird. Anwendung:

//bootstrap/app.php
$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

//Application: 构造方法
public function __construct($basePath = null)
{
    if ($basePath) {
        $this->setBasePath($basePath);
    }

    $this->registerBaseBindings();

    $this->registerBaseServiceProviders();

    $this->registerCoreContainerAliases();
}

//Application: 注册基础的服务提供器
protected function registerBaseServiceProviders()
{
    $this->register(new EventServiceProvider($this));

    $this->register(new LogServiceProvider($this));

    $this->register(new RoutingServiceProvider($this));
}

//\Illuminate\Routing\RoutingServiceProvider: 绑定router到服务容器
protected function registerRouter()
{
    $this->app->singleton('router', function ($app) {
        return new Router($app['events'], $app);
    });
}
Nach dem Login kopieren

Durch den obigen Code wissen wir das Die von Route aufgerufenen statischen Methoden entsprechen den Methoden in der Klasse IlluminateRoutingRouter. Die Klasse Router enthält Methoden zur Routenregistrierung, Adressierung und Planung.

Sehen wir uns an, wie dies in Laravel in den Phasen der Routing-Registrierung, des Ladens und der Adressierung implementiert wird.

Routen laden

Bevor Sie eine Route registrieren, müssen Sie zuerst die Routing-Datei laden. Die Routing-Datei wird in der AppProvidersRouteServiceProviderBoot-Methode dieses Serveranbieters geladen:

class RouteServiceProvider extends ServiceProvider
{
    public function boot()
    {
        parent::boot();
    }

    public function map()
    {
        $this->mapApiRoutes();

        $this->mapWebRoutes();
    }

    protected function mapWebRoutes()
    {
        Route::middleware('web')
             ->namespace($this->namespace)
             ->group(base_path('routes/web.php'));
    }

    protected function mapApiRoutes()
    {
        Route::prefix('api')
             ->middleware('api')
             ->namespace($this->namespace)
             ->group(base_path('routes/api.php'));
    }
}
Nach dem Login kopieren
namespace Illuminate\Foundation\Support\Providers;

class RouteServiceProvider extends ServiceProvider
{

    public function boot()
    {
        $this->setRootControllerNamespace();

        if ($this->app->routesAreCached()) {
            $this->loadCachedRoutes();
        } else {
            $this->loadRoutes();

            $this->app->booted(function () {
                $this->app['router']->getRoutes()->refreshNameLookups();
                $this->app['router']->getRoutes()->refreshActionLookups();
            });
        }
    }

    protected function loadCachedRoutes()
    {
        $this->app->booted(function () {
            require $this->app->getCachedRoutesPath();
        });
    }

    protected function loadRoutes()
    {
        if (method_exists($this, 'map')) {
            $this->app->call([$this, 'map']);
        }
    }
}

class Application extends Container implements ApplicationContract, HttpKernelInterface
{
    public function routesAreCached()
    {
        return $this['files']->exists($this->getCachedRoutesPath());
    }

    public function getCachedRoutesPath()
    {
        return $this->bootstrapPath().'/cache/routes.php';
    }
}
Nach dem Login kopieren

Laravel sucht zunächst nach der Cache-Datei der Route. Wenn keine Cache-Datei vorhanden ist, wird die Route geladen. Die Cache-Datei befindet sich normalerweise in der Datei bootstrap/cache/routes.php. Die
-Methode „loadRoutes“ ruft die Kartenmethode auf, um die Routen in die Routendatei zu laden. Die Kartenfunktion befindet sich in der Klasse AppProvidersRouteServiceProvider, die von IlluminateFoundationSupportProvidersRouteServiceProvider erbt. Durch die Kartenmethode können wir sehen, dass Laravel das Routing in zwei große Gruppen unterteilt: API und Web. Die Routen für diese beiden Teile werden jeweils in zwei Dateien geschrieben: Routen/web.php und Routen/api.php.

In Laravel 5.5 werden Routen in mehreren Dateien abgelegt. Die vorherige Version befand sich in der Datei app/Http/routes.php. Das Einfügen in mehrere Dateien kann die Verwaltung von API-Routen und WEB-Routen erleichtern.

Routenregistrierung

Wir verwenden normalerweise die Routenfassade, um die statische Methode get, post aufzurufen , head, options, put, patch, delete ... usw., um Routen zu registrieren. Wie oben erwähnt, rufen diese statischen Methoden tatsächlich Methoden in der Router-Klasse auf:

public function get($uri, $action = null)
{
    return $this->addRoute(['GET', 'HEAD'], $uri, $action);
}

public function post($uri, $action = null)
{
    return $this->addRoute('POST', $uri, $action);
}
....
Nach dem Login kopieren

Okay, Sie können das sehen Die Registrierung von Routen wird vollständig von der Methode addRoute der Router-Klasse abgewickelt:

//注册路由到RouteCollection
protected function addRoute($methods, $uri, $action)
{
    return $this->routes->add($this->createRoute($methods, $uri, $action));
}

//创建路由
protected function createRoute($methods, $uri, $action)
{
    if ($this->actionReferencesController($action)) {
        //controller@action类型的路由在这里要进行转换
        $action = $this->convertToControllerAction($action);
    }

    $route = $this->newRoute(
        $methods, $this->prefix($uri), $action
    );

    if ($this->hasGroupStack()) {
        $this->mergeGroupAttributesIntoRoute($route);
    }

    $this->addWhereClausesToRoute($route);

    return $route;
}

protected function convertToControllerAction($action)
{
    if (is_string($action)) {
        $action = ['uses' => $action];
    }

    if (! empty($this->groupStack)) {        
        $action['uses'] = $this->prependGroupNamespace($action['uses']);
    }
    
    $action['controller'] = $action['uses'];

    return $action;
}
Nach dem Login kopieren

Der dritte Aktionsparameter, der bei der Registrierung einer Route an addRoute übergeben wird, kann ein Abschluss, eine Zeichenfolge oder ein Array sein, und das Array ist Ähnlich wie ['uses' => 'Controller@action', 'middleware' => '...']. Wenn die Aktion eine Route vom Typ Controller@action ist, wird sie in ein Aktionsarray umgewandelt. Nach der Ausführung von „convertToControllerAction“ lautet der Inhalt der Aktion:

[
    'uses' => 'App\Http\Controllers\SomeController@someAction',
    'controller' => 'App\Http\Controllers\SomeController@someAction'
]
Nach dem Login kopieren

Zum Namen des Controllers hinzugefügt, um einen vollständigen Controller-Klassennamen und ein Aktionsarray zu erstellen. Der nächste Schritt besteht darin, eine Route zu erstellen. Verwenden Sie zum Erstellen einer Route die angegebene HTTP-Anforderungsmethode, den URI-String und das Aktionsarray eine Instanz der IlluminateRoutingRoute-Klasse:

protected function newRoute($methods, $uri, $action)
{
    return (new Route($methods, $uri, $action))
                ->setRouter($this)
                ->setContainer($this->container);
}
Nach dem Login kopieren

Nachdem die Route erstellt wurde, fügen Sie die Route zur RouteCollection hinzu:

protected function addRoute($methods, $uri, $action)
{
    return $this->routes->add($this->createRoute($methods, $uri, $action));
}
Nach dem Login kopieren

Das $routes-Attribut des Routers ist eine RouteCollection Beim Hinzufügen einer Route zum RouteCollection-Objekt speichern die Routen-, allRoutes-, nameList- und actionList-Attribute

class RouteCollection implements Countable, IteratorAggregate
{
    public function add(Route $route)
    {
        $this->addToCollections($route);

        $this->addLookups($route);

        return $route;
    }
    
    protected function addToCollections($route)
    {
        $domainAndUri = $route->getDomain().$route->uri();

        foreach ($route->methods() as $method) {
            $this->routes[$method][$domainAndUri] = $route;
        }

        $this->allRoutes[$method.$domainAndUri] = $route;
    }
    
    protected function addLookups($route)
    {
        $action = $route->getAction();

        if (isset($action['as'])) {
            //如果时命名路由,将route对象映射到以路由名为key的数组值中方便查找
            $this->nameList[$action['as']] = $route;
        }

        if (isset($action['controller'])) {
            $this->addToActionList($action, $route);
        }
    }

}
Nach dem Login kopieren

Die vier Attribute von RouteCollection

routes die Zuordnung zwischen HTTP-Anforderungsmethoden und Routing Objekte:

[
    'GET' => [
        $routeUri1 => $routeObj1
        ...
    ]
    ...
]
Nach dem Login kopieren

Inhalt im allRoutes-Attribut gespeichert Beim Programmieren des zweistelligen Arrays im Routenattribut in das einstellige Array:

[
    'GET' . $routeUri1 => $routeObj1
    'GET' . $routeUri2 => $routeObj2
    ...
]
Nach dem Login kopieren

nameList ist eine Zuordnungstabelle zwischen Routen Namen und Routing-Objekte

[
    $routeName1 => $routeObj1
    ...
]
Nach dem Login kopieren

actionList ist eine Routing-Steuerung. Die Zuordnungstabelle zwischen der Handler-Methodenzeichenfolge und dem Routing-Objekt

[
    'App\Http\Controllers\ControllerOne@ActionOne' => $routeObj1
]
Nach dem Login kopieren

sieht folgendermaßen aus und die Route wird registriert.

Routing-Adressierung

Im Artikel über Middleware haben wir gesagt, dass die HTTP-Anfrage das Ziel erreicht, nachdem sie die Voroperation der Middleware auf dem Pipeline-Kanal durchlaufen hat:

//Illuminate\Foundation\Http\Kernel
class Kernel implements KernelContract
{
    protected function sendRequestThroughRouter($request)
    {
        $this->app->instance('request', $request);

        Facade::clearResolvedInstance('request');

        $this->bootstrap();

        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }
    
    protected function dispatchToRouter()
    {
        return function ($request) {
            $this->app->instance('request', $request);

            return $this->router->dispatch($request);
        };
    }
    
}
Nach dem Login kopieren

Aus dem obigen Code können Sie ersehen, dass das Ziel der Pipeline der von der Funktion „dispatchToRouter“ zurückgegebene Abschluss ist:

$destination = function ($request) {
    $this->app->instance('request', $request);
    return $this->router->dispatch($request);
};
Nach dem Login kopieren

Die Dispatch-Methode des Routers wird im Abschluss und in der Route aufgerufen Die Adressierung erfolgt in der ersten Phase des Versands, findRoute. Hier:

class Router implements RegistrarContract, BindingRegistrar
{    
    public function dispatch(Request $request)
    {
        $this->currentRequest = $request;

        return $this->dispatchToRoute($request);
    }
    
    public function dispatchToRoute(Request $request)
    {
        return $this->runRoute($request, $this->findRoute($request));
    }
    
    protected function findRoute($request)
    {
        $this->current = $route = $this->routes->match($request);

        $this->container->instance(Route::class, $route);

        return $route;
    }
    
}
Nach dem Login kopieren

Die Aufgabe, Routen zu finden, wird von RouteCollection übernommen. Diese Funktion ist für den Abgleich von Routen und die Bindung der URL-Parameter der Anfrage an die Routen verantwortlich :

class RouteCollection implements Countable, IteratorAggregate
{
    public function match(Request $request)
    {
        $routes = $this->get($request->getMethod());

        $route = $this->matchAgainstRoutes($routes, $request);

        if (! is_null($route)) {
            return $route->bind($request);
        }

        $others = $this->checkForAlternateVerbs($request);

        if (count($others) > 0) {
            return $this->getRouteForMethods($request, $others);
        }

        throw new NotFoundHttpException;
    }

    protected function matchAgainstRoutes(array $routes, $request, $includingMethod = true)
    {
        return Arr::first($routes, function ($value) use ($request, $includingMethod) {
            return $value->matches($request, $includingMethod);
        });
    }
}

class Route
{
    public function matches(Request $request, $includingMethod = true)
    {
        $this->compileRoute();

        foreach ($this->getValidators() as $validator) {
            if (! $includingMethod && $validator instanceof MethodValidator) {
                continue;
            }

            if (! $validator->matches($this, $request)) {
                return false;
            }
        }

        return true;
    }
}
Nach dem Login kopieren

$routes = $this->get($request->getMethod());会先加载注册路由阶段在RouteCollection里生成的routes属性里的值,routes中存放了HTTP请求方法与路由对象的映射。

然后依次调用这堆路由里路由对象的matches方法, matches方法, matches方法里会对HTTP请求对象进行一些验证,验证对应的Validator是:UriValidator、MethodValidator、SchemeValidator、HostValidator。
在验证之前在$this->compileRoute()里会将路由的规则转换成正则表达式。

UriValidator主要是看请求对象的URI是否与路由的正则规则匹配能匹配上:

class UriValidator implements ValidatorInterface
{
    public function matches(Route $route, Request $request)
    {
        $path = $request->path() == '/' ? '/' : '/'.$request->path();

        return preg_match($route->getCompiled()->getRegex(), rawurldecode($path));
    }
}
Nach dem Login kopieren

MethodValidator验证请求方法, SchemeValidator验证协议是否正确(http|https), HostValidator验证域名, 如果路由中不设置host属性,那么这个验证不会进行。

一旦某个路由通过了全部的认证就将会被返回,接下来就要将请求对象URI里的路径参数绑定复制给路由参数:

路由参数绑定

class Route
{
    public function bind(Request $request)
    {
        $this->compileRoute();

        $this->parameters = (new RouteParameterBinder($this))
                        ->parameters($request);

        return $this;
    }
}

class RouteParameterBinder
{
    public function parameters($request)
    {
        $parameters = $this->bindPathParameters($request);

        if (! is_null($this->route->compiled->getHostRegex())) {
            $parameters = $this->bindHostParameters(
                $request, $parameters
            );
        }

        return $this->replaceDefaults($parameters);
    }
    
    protected function bindPathParameters($request)
    {
            preg_match($this->route->compiled->getRegex(), '/'.$request->decodedPath(), $matches);

            return $this->matchToKeys(array_slice($matches, 1));
    }
    
    protected function matchToKeys(array $matches)
    {
        if (empty($parameterNames = $this->route->parameterNames())) {
            return [];
        }

        $parameters = array_intersect_key($matches, array_flip($parameterNames));

        return array_filter($parameters, function ($value) {
            return is_string($value) && strlen($value) > 0;
        });
    }
}
Nach dem Login kopieren

赋值路由参数完成后路由寻址的过程就结束了,结下来就该运行通过匹配路由中对应的控制器方法返回响应对象了。

class Router implements RegistrarContract, BindingRegistrar
{    
    public function dispatch(Request $request)
    {
        $this->currentRequest = $request;

        return $this->dispatchToRoute($request);
    }
    
    public function dispatchToRoute(Request $request)
    {
        return $this->runRoute($request, $this->findRoute($request));
    }
    
    protected function runRoute(Request $request, Route $route)
    {
        $request->setRouteResolver(function () use ($route) {
            return $route;
        });

        $this->events->dispatch(new Events\RouteMatched($route, $request));

        return $this->prepareResponse($request,
            $this->runRouteWithinStack($route, $request)
        );
    }
    
}
Nach dem Login kopieren

这里我们主要介绍路由相关的内容,在runRoute的过程通过上面的源码可以看到其实也很复杂, 会收集路由和控制器里的中间件,将请求通过中间件过滤才会最终调用控制器方法来生成响应对象,这个过程还会设计到我们以前介绍过的中间件过滤、服务解析、依赖注入方面的信息,如果在看源码时有不懂的地方可以翻看我之前写的文章。

  1. 依赖注入

  2. 服务容器

  3. 中间件

相关推荐:

如何实现Laravel路由中不固定数量的参数

Laravel路由设定和子路由设定实例分析_PHP

laravel路由问题


Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der Laravel-Routing-Route. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage