# 路由服务提供者

Testing Is Documentation

tests/Router/RouterProviderTest.php

路由主要由路由服务来接入框架,可以做一些设置。

namespace Common\Infra\Provider;

use Admin\App\Middleware\Auth as AdminAuth;
use Admin\App\Middleware\Cors;
use Leevel\Auth\Middleware\Auth;
use Leevel\Debug\Middleware\Debug;
use Leevel\Di\IContainer;
use Leevel\Log\Middleware\Log;
use Leevel\Router\RouterProvider;
use Leevel\Session\Middleware\Session;
use Leevel\Throttler\Middleware\Throttler;

class Router extends RouterProvider
{
    /**
     * 控制器相对目录.
     *
     * @var string
     */
    protected string $controllerDir = 'App\\Controller';

    /**
     * 中间件分组.
     *
     * - 分组可以很方便地批量调用组件.
     *
     * @var array
     */
    protected array $middlewareGroups = [
        // web 请求中间件
        'web' => [
            'session',
        ],

        // api 请求中间件
        'api' => [
            // API 限流,可以通过网关来做限流更高效,如果需要去掉注释即可
            // 'throttler:60,60',
        ],

        // 公共请求中间件
        'common' => [
            'log',
        ],
    ];

    /**
     * 中间件别名.
     *
     * - HTTP 中间件提供一个方便的机制来过滤进入应用程序的 HTTP 请求
     * - 例外在应用执行结束后响应环节也会调用 HTTP 中间件.
     *
     * @var array
     */
    protected array $middlewareAlias = [
        'auth'              => Auth::class,
        'cors'              => Cors::class,
        'admin_auth'        => AdminAuth::class,
        'debug'             => Debug::class,
        'log'               => Log::class,
        'session'           => Session::class,
        'throttler'         => Throttler::class,
    ];

    /**
     * 基础路径.
     *
     * @var array
     */
    protected array $basePaths = [
        '*' => [
            'middlewares' => 'common',
        ],
        'foo/*world' => [
        ],
        'api/test' => [
            'middlewares' => 'api',
        ],
        ':admin/*' => [
            'middlewares' => 'admin_auth,cors',
        ],
        'options/index' => [
            'middlewares' => 'cors',
        ],
        'admin/show' => [
            'middlewares' => 'auth',
        ],
    ];

    /**
     * 分组.
     *
     * @var array
     */
    protected array $groups = [
        'pet'     => [],
        'store'   => [],
        'user'    => [],
        '/api/v1' => [
            'middlewares' => 'api',
        ],
        'api/v2' => [
            'middlewares' => 'api',
        ],
        '/web/v1' => [
            'middlewares' => 'web',
        ],
        'web/v2' => [
            'middlewares' => 'web',
        ],
    ];

    /**
     * 创建一个服务容器提供者实例.
     */
    public function __construct(IContainer $container)
    {
        parent::__construct($container);
        if ($container->make('app')->isDebug()) {
            $this->middlewareGroups['common'][] = 'debug';
        }
    }

    /**
     * bootstrap.
     */
    public function bootstrap(): void
    {
        parent::bootstrap();
    }

    /**
     * 返回路由.
     */
    public function getRouters(): array
    {
        return parent::getRouters();
    }
}

Uses

<?php

use Leevel\Di\Container;
use Leevel\Kernel\App;
use Leevel\Router\Router;
use Leevel\Router\RouterProvider;
use Leevel\Router\Url;
use Tests\Router\Middlewares\Demo1;
use Tests\Router\Middlewares\Demo2;
use Tests\Router\Middlewares\Demo3;

# 基本使用

QueryPHP 路由最终结果主要由 base_pathsgroupsrouters 构成。

fixture 定义

路由服务提供者 Tests\Router\RouterProvider1

namespace Tests\Router;

class RouterProvider1 extends RouterProvider
{
    protected string $controllerDir = 'Tests\\Router\\Apps';

    protected array $middlewareGroups = [
        'group1' => [
            'demo1',
            'demo2',
        ],
        'group2' => [
            'demo1',
            'demo3:10,world',
        ],
        'group3' => [
            'demo1',
            'demo2',
            'demo3:10,world',
        ],
    ];

    protected array $middlewareAlias = [
        'demo1' => Demo1::class,
        'demo2' => Demo2::class,
        'demo3' => Demo3::class,
    ];

    protected array $basePaths = [];

    protected array $groups = [
        'pet'     => [],
        'store'   => [],
        'user'    => [],
        '/api/v1' => [
            'middlewares' => 'group1',
        ],
        '/api/v2' => [
            'middlewares' => 'group2',
        ],
        '/api/v3' => [
            'middlewares' => 'demo1,demo3:30,world',
        ],
        '/api/v3' => [
            'middlewares' => ['demo1', 'group3'],
        ],
        '/api/v4' => [
            'middlewares' => 'notFound',
        ],
    ];

    public function bootstrap(): void
    {
        parent::bootstrap();
    }

    public function getRouters(): array
    {
        return parent::getRouters();
    }
}

路由注解缓存结果 tests/Router/Apps/AppScanRouter/data.json

{
    "base_paths": {
        "\/^\\\/api\\\/v1(\\S*)\\\/$\/": {
            "middlewares": {
                "handle": [
                    "Tests\\Router\\Middlewares\\Demo2@handle"
                ],
                "terminate": [
                    "Tests\\Router\\Middlewares\\Demo1@terminate",
                    "Tests\\Router\\Middlewares\\Demo2@terminate"
                ]
            }
        },
        "\/^\\\/api\\\/v2(\\S*)\\\/$\/": {
            "middlewares": {
                "handle": [
                    "Tests\\Router\\Middlewares\\Demo3@handle:10,world"
                ],
                "terminate": [
                    "Tests\\Router\\Middlewares\\Demo1@terminate"
                ]
            }
        },
        "\/^\\\/api\\\/v3(\\S*)\\\/$\/": {
            "middlewares": {
                "handle": [
                    "Tests\\Router\\Middlewares\\Demo2@handle",
                    "Tests\\Router\\Middlewares\\Demo3@handle:10,world"
                ],
                "terminate": [
                    "Tests\\Router\\Middlewares\\Demo1@terminate",
                    "Tests\\Router\\Middlewares\\Demo2@terminate"
                ]
            }
        }
    },
    "groups": [
        "\/pet",
        "\/store",
        "\/user",
        "\/api\/v1",
        "\/api\/v2",
        "\/api\/v3",
        "\/api\/v4"
    ],
    "routers": {
        "get": {
            "a": {
                "\/api\/v1": {
                    "\/api\/v1\/petLeevel\/{petId:[A-Za-z]+}\/": {
                        "bind": "\\Tests\\Router\\Apps\\AppScanRouter\\Controllers\\Pet@petLeevel",
                        "var": [
                            "petId"
                        ]
                    },
                    "regex": [
                        "~^(?|\/api\/v1\/petLeevel\/([A-Za-z]+)\/)$~x"
                    ],
                    "map": [
                        {
                            "2": "\/api\/v1\/petLeevel\/{petId:[A-Za-z]+}\/"
                        }
                    ]
                }
            },
            "static": {
                "\/web\/petLeevelWithPort\/": {
                    "port": 9527,
                    "bind": "\\Tests\\Router\\Apps\\AppScanRouter\\Controllers\\Pet@petLeevelWithPort"
                }
            }
        }
    }
}
public function testBaseUse(): void
{
    $container = Container::singletons();
    $app = new App($container, '');
    $app->setAppPath(__DIR__.'/Apps/AppScanRouter');
    $app->setPath(__DIR__.'/Apps/AppScanRouter');
    $app->setRouterCachedPath(__DIR__.'/router_cached.php');

    $container->instance('app', $app);
    $container->instance('router', $router = $this->createRouter($container));

    $provider = new RouterProvider1($container);

    $this->assertNull($provider->register());
    $this->assertNull($provider->bootstrap());

    $data = file_get_contents(__DIR__.'/Apps/AppScanRouter/data.json');

    $this->assertSame(
        $data,
        $this->varJson(
            [
                'base_paths'  => $router->getBasePaths(),
                'groups'      => $router->getGroups(),
                'routers'     => $router->getRouters(),
            ]
        )
    );

    Container::singletons()->clear();
}