yiisoft/router

Yii 路由器

3.1.0 2024-02-20 11:39 UTC

README

Yii

Yii Router


Latest Stable Version Total Downloads Build status Code coverage Mutation testing badge static analysis type-coverage

该包提供了兼容PSR-7的请求路由和准备在应用程序中使用PSR-15中间件。该包不是从头开始实现路由,而是提供了一个配置路由的接口,并可以与适配器包一起使用。目前,唯一的适配器是FastRoute

功能

  • 支持HTTP方法、主机和默认值的URL匹配和生成。
  • 良好的IDE支持,用于定义路由。
  • 支持无限嵌套的路由分组。
  • 支持为单个路由和分组提供中间件。
  • 准备用于路由匹配的中间件。
  • 方便的CurrentRoute服务,用于保存有关最后匹配路由的信息。
  • 开箱即用的CORS中间件支持。

需求

  • PHP 8.0或更高版本。

安装

可以使用Composer安装此包。

composer require yiisoft/router

此外,您还需要一个适配器,例如FastRoute

定义路由和URL匹配

路由器的常见用法如下

use Yiisoft\Router\CurrentRoute;
use Yiisoft\Router\Group;
use Yiisoft\Router\Route;
use Yiisoft\Router\RouteCollection;
use Yiisoft\Router\RouteCollectorInterface;
use Yiisoft\Router\UrlMatcherInterface;
use Yiisoft\Router\Fastroute\UrlMatcher;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

// Define routes
$routes = [
    Route::get('/')
        ->action(static function (ServerRequestInterface $request, RequestHandlerInterface $next) use ($responseFactory) {
            $response = $responseFactory->createResponse();
            $response
                ->getBody()
                ->write('You are at homepage.');
            return $response;
        }),
    Route::get('/test/{id:\w+}')
        ->action(static function (CurrentRoute $currentRoute, RequestHandlerInterface $next) use ($responseFactory) {
            $id = $currentRoute->getArgument('id');
    
            $response = $responseFactory->createResponse();
            $response
                ->getBody()
                ->write('You are at test with argument ' . $id);
            return $response;
        })
];

// Add routes defined to route collector
$collector = $container->get(RouteCollectorInterface::class);
$collector->addGroup(Group::create(null)->routes($routes));

// Initialize URL matcher
/** @var UrlMatcherInterface $urlMatcher */
$urlMatcher = new UrlMatcher(new RouteCollection($collector));

// Do the match against $request which is PSR-7 ServerRequestInterface. 
$result = $urlMatcher->match($request);

if (!$result->isSuccess()) {
     // 404
}

// $result->arguments() contains arguments from the match.

// Run middleware assigned to a route found.
$response = $result->process($request, $notFoundHandler);

注意:尽管UrlGeneratorInterfaceUrlMatcherInterface对所有可用的适配器都是通用的,但某些功能和,尤其是模式语法可能会有所不同。有关使用和配置详细信息,请参阅特定适配器文档。本文件中的所有示例均为FastRoute适配器

中间件使用

为了简化基于PSR中间件的应用程序的使用,提供了现成的中间件

$router = $container->get(Yiisoft\Router\UrlMatcherInterface::class);
$responseFactory = $container->get(\Psr\Http\Message\ResponseFactoryInterface::class);

$routerMiddleware = new Yiisoft\Router\Middleware\Router($router, $responseFactory, $container);

// Add middleware to your middleware handler of choice.

在路由匹配的情况下,如果匹配,则路由器中间件执行附加到路由的处理器中间件。如果没有匹配,则下一个应用程序中间件处理请求。

路由

路由可以匹配一个或多个HTTP方法:GETPOSTPUTDELETEPATCHHEADOPTIONS。为创建特定方法的路由提供了相应的静态方法。如果要一次处理多个方法,可以使用methods()

use Yiisoft\Router\Route;

Route::delete('/post/{id}')
    ->name('post-delete')
    ->action([PostController::class, 'actionDelete']);
    
Route::methods([Method::GET, Method::POST], '/page/add')
    ->name('page-add')
    ->action([PageController::class, 'actionAdd']);

如果您要根据路由及其参数生成URL,请使用name()给它命名。有关详细信息,请参阅“创建URL”。

在上面的示例中,action()是主要的中间件定义,当调用匹配结果的process()方法时最后被调用。中间件的执行方式和接受的中间件格式由使用的中间件调度器定义。有关中间件示例,请参阅yiisoft/middleware-dispatcher的readme

如果路由仅应用于特定的主机,可以定义如下

use Yiisoft\Router\Route;

Route::get('/special')
    ->name('special')
    ->action(SpecialAction::class)
    ->host('https://yiiframework.cn');

可以通过defaults()方法提供参数的默认值

use Yiisoft\Router\Route;

Route::get('/api[/v{version}]')
    ->name('api-index')
    ->action(ApiAction::class)
    ->defaults(['version' => 1]);

在上面的示例中,我们指定如果匹配过程中没有从URL中获取到“version”,则它将是1

除了操作外,还可以定义在操作之前执行的其他中间件

use Yiisoft\Router\Route;

Route::methods([Method::GET, Method::POST], '/page/add')
    ->middleware(Authentication::class)
    ->middleware(ExtraHeaders::class)
    ->action([PostController::class, 'add'])
    ->name('blog/add');

这通常用于可以用于多个路由的特定操作,例如身份验证。

如果需要先执行中间件或从路由中移除现有中间件,可以使用 prependMiddleware()disableMiddleware()

如果您想将来自多个来源的路由组合起来,并希望最后一个路由优先于现有的路由,请将其标记为“覆盖”。

use Yiisoft\Router\Route;

Route::get('/special')
    ->name('special')
    ->action(SpecialAction::class)
    ->override();

路由分组

路由可以进行分组。这对于API端点等情况非常有用。

use \Yiisoft\Router\Route;
use \Yiisoft\Router\Group;
use \Yiisoft\Router\RouteCollectorInterface;

// for obtaining router see adapter package of choice readme
$collector = $container->get(RouteCollectorInterface::class);
    
$collector->addGroup(
    Group::create('/api')
        ->middleware(ApiAuthentication::class)
        ->host('https://example.com')
        ->routes([
            Route::get('/comments'),
            Group::create('/posts')->routes([
                Route::get('/list'),
            ]),
        ])
);

组可以有一个前缀,例如上面的 /api。该前缀应用于每个组的路由,无论是匹配还是生成URL时。

类似于单个路由,组也可以使用 middleware()prependMiddleware()disableMiddleware() 管理一组中间件。这些中间件在匹配路由的自身中间件和操作之前执行。

如果指定了主机,组中的所有路由只有当主机匹配时才会匹配。

自动的OPTIONS响应和CORS

默认情况下,根据定义的路由自动响应该的OPTIONS请求。

HTTP/1.1 204 No Content
Allow: GET, HEAD

这通常没问题,除非您需要CORS头信息。在这种情况下,您可以添加一个处理它的中间件,例如 tuupola/cors-middleware

use Yiisoft\Router\Group;
use \Tuupola\Middleware\CorsMiddleware;

return [
    Group::create('/api')
        ->withCors(CorsMiddleware::class)
        ->routes([
          // ...
        ]
    );
];

创建URL

可以使用 UrlGeneratorInterface::generate() 创建URL。假设一个路由定义如下

use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ResponseFactoryInterface;
use Yiisoft\Yii\Http\Handler\NotFoundHandler;
use Yiisoft\Yii\Runner\Http\SapiEmitter;
use Yiisoft\Yii\Runner\Http\ServerRequestFactory;
use Yiisoft\Router\CurrentRoute;
use Yiisoft\Router\Route;
use Yiisoft\Router\RouteCollection;
use Yiisoft\Router\RouteCollectorInterface;
use Yiisoft\Router\Fastroute\UrlMatcher;


$request = $container
    ->get(ServerRequestFactory::class)
    ->createFromGlobals();
$responseFactory = $container->get(ResponseFactoryInterface::class);
$notFoundHandler = new NotFoundHandler($responseFactory);
$collector = $container->get(RouteCollectorInterface::class);
$collector->addRoute(
    Route::get('/test/{id:\w+}')
        ->action(static function (CurrentRoute $currentRoute, RequestHandlerInterface $next) use ($responseFactory) {
            $id = $currentRoute->getArgument('id');
            $response = $responseFactory->createResponse();
            $response
                ->getBody()
                ->write('You are at test with argument ' . $id);

           return $response;
        })
        ->name('test')
);
$router = new UrlMatcher(new RouteCollection($collector));
$route = $router->match($request);
$response = $route->process($request, $notFoundHandler);
$emitter = new SapiEmitter();
$emitter->emit($response, $request->getMethod() === Method::HEAD);

然后可以这样获取该URL

use Yiisoft\Router\UrlGeneratorInterface;

function getUrl(UrlGeneratorInterface $urlGenerator, $parameters = [])
{
    return $urlGenerator->generate('test', $parameters);
}

可以使用 UrlGeneratorInterface::generateAbsolute() 生成绝对URL

use Yiisoft\Router\UrlGeneratorInterface;

function getUrl(UrlGeneratorInterface $urlGenerator, $parameters = [])
{
    return $urlGenerator->generateAbsolute('test', $parameters);
}

此外,还有一个方便的 UrlGeneratorInterface::generateFromCurrent() 方法。它允许生成一个当前URL的修改版本。

use Yiisoft\Router\UrlGeneratorInterface;

function getUrl(UrlGeneratorInterface $urlGenerator, $id)
{
    return $urlGenerator->generateFromCurrent(['id' => 42]);
}

在上面的例子中,ID将被替换为42,其余参数保持不变。这对于修改用于过滤和/或排序的URL非常有用。

获取当前路由信息

对于此类路由

use \Yiisoft\Router\Route;

$routes = [
    Route::post('/post/{id:\d+}')
        ->action([PostController::class, 'actionEdit']),
];

可以通过以下方式获取信息

use Psr\Http\Message\ResponseInterface
use Psr\Http\Message\UriInterface;
use Yiisoft\Router\CurrentRoute;
use Yiisoft\Router\Route;

final class PostController
{   
    public function actionEdit(CurrentRoute $currentRoute): ResponseInterface
    {
        $postId = $currentRoute->getArgument('id');
        if ($postId === null) {
            throw new \InvalidArgumentException('Post ID is not specified.');
        }
        
        // ...
    
    }
}

除了常用的 getArgument() 方法外,还有以下方法可用

  • getArguments() - 一次性获取所有参数。
  • getName() - 获取路由名称。
  • getHost() - 获取路由主机。
  • getPattern() - 获取路由模式。
  • getMethods() - 获取路由方法。
  • getUri() - 获取当前URI。

文档

如果您需要帮助或有问题,Yii 论坛 是一个好地方。您还可以查看其他 Yii 社区资源

许可

Yii路由器是免费软件。它根据BSD许可协议发布。有关更多信息,请参阅LICENSE

Yii软件 维护。

支持项目

Open Collective

关注更新

Official website Twitter Telegram Facebook Slack