Rumah  >  Artikel  >  rangka kerja php  >  apa itu penghalaan laravel

apa itu penghalaan laravel

青灯夜游
青灯夜游asal
2021-09-02 11:57:262687semak imbas

Dalam laravel, penghalaan ialah cara untuk dunia luar mengakses aplikasi Laravel, atau penghalaan mentakrifkan cara khusus aplikasi Laravel menyediakan perkhidmatan kepada dunia luar. Penghalaan akan menyerahkan permintaan pengguna kepada pengawal dan kaedah yang ditentukan untuk pemprosesan mengikut pelan yang telah dirancang sebelumnya.

apa itu penghalaan laravel

Persekitaran pengendalian tutorial ini: sistem Windows 7, versi Laravel 6, komputer DELL G3.

Penghalaan ialah cara untuk dunia luar mengakses aplikasi Laravel, atau penghalaan mentakrifkan cara khusus aplikasi Laravel menyediakan perkhidmatan kepada dunia luar: capaian yang betul boleh dicapai melalui URI yang ditentukan, kaedah permintaan HTTP, dan parameter penghalaan (pilihan) kepada pengendali yang ditakrifkan oleh laluan.

Sama ada pengendali yang sepadan dengan URI adalah penutupan mudah atau kaedah pengawal tidak mempunyai laluan yang sepadan, dunia luar tidak boleh mengaksesnya

Hari ini kita akan melihat bagaimana Laravel muncul dalam Reka Bentuk dan melaksanakan penghalaan.

Kami biasanya mentakrifkan laluan seperti berikut dalam fail penghalaan:

Route::get('/user', 'UsersController@index');

Dari laluan di atas, kami dapat mengetahui bahawa pelanggan meminta melalui HTTP GET Apabila URI adalah "/ pengguna", Laravel akhirnya akan menghantar permintaan kepada kaedah indeks kelas UsersController untuk pemprosesan, dan kemudian mengembalikan respons kepada klien dalam kaedah indeks.

Kelas Laluan yang digunakan semasa mendaftar laluan di atas dipanggil Facade dalam Laravel Ia menyediakan cara mudah untuk mengakses penghala perkhidmatan yang terikat pada bekas perkhidmatan Konsep reka bentuk dan pelaksanaan Facade Saya merancang untuk menulis blog yang berasingan siarkan tentang kaedah pada masa hadapan Di sini kita hanya perlu tahu bahawa kaedah statik fasad Route yang dipanggil sesuai dengan kaedah perkhidmatan penghala dalam bekas perkhidmatan, jadi anda juga boleh menganggap laluan di atas sebagai mendaftar seperti. ini:

app()->make('router')->get('user', 'UsersController@index');

Perkhidmatan penghala terikat pada bekas perkhidmatan dengan mendaftarkan RoutingServiceProvider dalam pembina apabila membuat instantiate aplikasi Aplikasi:

//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);
    });
}

Melalui kod di atas kita Kami tahu bahawa kaedah statik yang dipanggil oleh Route sepadan dengan kaedah dalam kelas IlluminateRoutingRouter The Router mengandungi kaedah yang berkaitan dengan pendaftaran laluan, pengalamatan dan penjadualan.

Mari kita lihat bagaimana ini dilaksanakan dalam laravel dari peringkat pendaftaran penghalaan, pemuatan dan pengalamatan.

Pemuatan laluan

Anda perlu memuatkan fail penghalaan sebelum mendaftarkan laluan Fail penghalaan dimuatkan dalam AppProvidersRouteServiceProviderbut pembekal pelayan ini Dimuatkan dalam kaedah:

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'));
    }
}
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';
    }
}

Laravel mula-mula mencari fail cache laluan tersebut. Jika tiada fail cache, ia akan memuatkan laluan. Fail cache biasanya dalam fail bootstrap/cache/routes.php. Kaedah
loadRoutes akan memanggil kaedah peta untuk memuatkan laluan dalam fail laluan Fungsi peta berada dalam kelas AppProvidersRouteServiceProvider, yang mewarisi daripada IlluminateFoundationSupportProvidersRouteServiceProvider. Melalui kaedah peta, kita dapat melihat bahawa laravel membahagikan penghalaan kepada dua kumpulan besar: api dan web. Laluan untuk kedua-dua bahagian ini ditulis dalam dua fail masing-masing: route/web.php dan route/api.php.

Dalam Laravel 5.5, laluan diletakkan dalam beberapa fail Versi sebelumnya berada dalam fail app/Http/routes.php. Meletakkannya dalam berbilang fail menjadikannya lebih mudah untuk mengurus penghalaan API dan penghalaan WEB

Pendaftaran laluan

kami Biasanya Route Facade digunakan untuk memanggil kaedah statik get, post, head, options, put, patch, delete...dsb untuk mendaftarkan laluan Seperti yang kami nyatakan di atas, kaedah statik ini sebenarnya memanggil kelas Router:

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);
}
....

Anda boleh melihat bahawa pendaftaran laluan dikendalikan oleh kaedah addRoute kelas penghala:

//注册路由到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;
}

Parameter ketiga yang diluluskan kepada addRoute apabila mendaftarkan laluan Tindakan boleh menjadi penutupan, a rentetan, atau tatasusunan adalah dalam bentuk ['uses' => 'Controller@action', 'middleware' => '...']. Jika tindakan adalah laluan jenis Controller@action, ia akan ditukar kepada tatasusunan tindakan Selepas convertToControllerAction dilaksanakan, kandungan tindakan ialah:

[
    'uses' => 'App\Http\Controllers\SomeController@someAction',
    'controller' => 'App\Http\Controllers\SomeController@someAction'
]

Anda boleh melihat bahawa ruang nama itu ditambahkan pada nama pengawal untuk membentuk nama kelas pengawal dan tatasusunan tindakan dibina. Langkah seterusnya ialah mencipta laluan Untuk mencipta laluan, gunakan kaedah permintaan HTTP yang ditentukan, rentetan URI dan tatasusunan tindakan contoh kelas IlluminateRoutingRoute:

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

Selepas laluan dibuat, tambahkan Route ke RouteCollection:

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

Atribut $routes bagi penghala ialah RouteCollection objek. Apabila menambah laluan pada objek RouteCollection, atribut laluan, allRoutes, nameList dan actionList

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);
        }
    }

}

Empat atribut RouteCollection

menyimpan pemetaan antara kaedah permintaan HTTP dan penghalaan objek:

[
    'GET' => [
        $routeUri1 => $routeObj1
        ...
    ]
    ...
]

kandungan yang disimpan dalam atribut allRoutes Apabila memprogram tatasusunan dua digit dalam atribut laluan ke dalam tatasusunan satu digit:

[
    'GET' . $routeUri1 => $routeObj1
    'GET' . $routeUri2 => $routeObj2
    ...
]

nameList ialah jadual pemetaan antara laluan nama dan objek penghalaan

[
    $routeName1 => $routeObj1
    ...
]

actionList ialah kawalan penghalaan Jadual pemetaan antara rentetan kaedah pengendali dan objek penghalaan

[
    'App\Http\Controllers\ControllerOne@ActionOne' => $routeObj1
]

adalah seperti ini dan laluan itu didaftarkan.

路由寻址

中间件的文章里我们说过HTTP请求在经过Pipeline通道上的中间件的前置操作后到达目的地:

//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);
        };
    }
    
}

上面代码可以看到Pipeline的destination就是dispatchToRouter函数返回的闭包:

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

在闭包里调用了router的dispatch方法,路由寻址就发生在dispatch的第一个阶段findRoute里:

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;
    }
    
}

寻找路由的任务由 RouteCollection 负责,这个函数负责匹配路由,并且把 request 的 url 参数绑定到路由中:

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

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

        if (! is_null($route)) {
            //找到匹配的路由后,将URI里的路径参数绑定赋值给路由(如果有的话)
            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;
    }
}

$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));
    }
}

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;
        });
    }
}

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

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)
        );
    }
    
    protected function runRouteWithinStack(Route $route, Request $request)
    {
        $shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
                            $this->container->make('middleware.disable') === true;
    //收集路由和控制器里应用的中间件
        $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route);

        return (new Pipeline($this->container))
                    ->send($request)
                    ->through($middleware)
                    ->then(function ($request) use ($route) {
                        return $this->prepareResponse(
                            $request, $route->run()
                        );
                    });
    
    }
    
}

namespace Illuminate\Routing;
class Route
{
    public function run()
    {
        $this->container = $this->container ?: new Container;
        try {
            if ($this->isControllerAction()) {
                return $this->runController();
            }
            return $this->runCallable();
        } catch (HttpResponseException $e) {
            return $e->getResponse();
        }
    }

}

这里我们主要介绍路由相关的内容,runRoute的过程通过上面的源码可以看到其实也很复杂, 会收集路由和控制器里的中间件,将请求通过中间件过滤才会最终到达目的地路由,执行目的路由地run()方法,里面会判断路由对应的是一个控制器方法还是闭包然后进行相应地调用,最后把执行结果包装成Response对象返回给客户端。这个过程还会涉及到我们以前介绍过的中间件过滤、服务解析、依赖注入方面的信息。

相关推荐:最新的五个Laravel视频教程

Atas ialah kandungan terperinci apa itu penghalaan laravel. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn