URI 路由

什么是 URI 路由?

URI 路由将 URI 与控制器的方法关联起来。

CodeIgniter 有两种路由方式:定义路由自动路由。 使用定义路由,可以手动定义路由,从而实现灵活的 URL。 自动路由根据约定,自动路由 HTTP 请求并执行相应的控制器方法,无需手动定义路由。

首先介绍定义路由。如需使用自动路由,请参阅 自动路由(改进版)

设置路由规则

路由规则定义在 app/Config/Routes.php 文件中。在该文件中,你会看到它创建了一个 RouteCollection 类的实例($routes),允许你指定自己的路由规则。路由可以使用占位符或正则表达式来定义。

定义路由时,需要选择与 HTTP 方法(请求方法)对应的方法。如果预期接收 GET 请求,使用 get() 方法:

<?php

$routes->get('/', 'Home::index');

路由接收左侧的 路由路径 (相对于 BaseURL 的 URI 路径,/),并将其映射到右侧的 路由处理程序 (控制器和方法 Home::index),同时传递应传递给控制器的任何参数。

控制器和方法的列出方式应与使用静态方法的方式相同,即用双冒号分隔类和方法,如 Users::list

如果该方法需要传递参数,则在方法名之后列出参数,用正斜杠分隔:

<?php

// Calls $Users->list()
$routes->get('users', 'Users::list');

// Calls $Users->list(1, 23)
$routes->get('users/1/23', 'Users::list/1/23');

示例

以下是一些基本的路由示例。

URL 第一段包含 journals 单词,将映射到 \App\Controllers\Blogs 类和 默认方法 (通常为 index()):

<?php

$routes->get('journals', 'Blogs');

URL 包含 blog/joe 段,将映射到 \App\Controllers\Blogs 类和 users() 方法。ID 将被设置为 34

<?php

$routes->get('blog/joe', 'Blogs::users/34');

URL 以 product 作为第一段,第二段为任意内容,将映射到 \App\Controllers\Catalog 类和 productLookup() 方法:

<?php

$routes->get('product/(:segment)', 'Catalog::productLookup');

URL 以 product 作为第一段,第二段为数字,将映射到 \App\Controllers\Catalog 类和 productLookupByID() 方法,将匹配结果作为变量传递给方法:

<?php

$routes->get('product/(:num)', 'Catalog::productLookupByID/$1');

HTTP 方法路由

可以使用任何标准 HTTP 方法(GET、POST、PUT、DELETE、OPTIONS 等):

<?php

$routes->post('products', 'Product::feature');
$routes->put('products/1', 'Product::feature');
$routes->delete('products/1', 'Product::feature');

可以通过将多个方法作为数组传递给 match() 方法来指定路由应匹配的多个方法:

<?php

$routes->match(['GET', 'PUT'], 'products', 'Product::feature');

指定路由处理程序

控制器的命名空间

当以字符串形式指定控制器和方法名时,如果控制器名称未以 \ 开头,系统会自动添加 默认命名空间

<?php

// Routes to \App\Controllers\Api\Users::update()
$routes->post('api/users', 'Api\Users::update');

如果开头有 \,则被视为完全限定的类名:

<?php

// Routes to \Acme\Blog\Controllers\Home::list()
$routes->get('blog', '\Acme\Blog\Controllers\Home::list');

也可以使用 namespace 选项指定命名空间:

<?php

// Routes to \Admin\Users::index()
$routes->get('admin/users', 'Users::index', ['namespace' => 'Admin']);

详情请参阅 分配命名空间

数组可调用语法

Added in version 4.2.0.

自 v4.2.0 起,可以使用数组可调用语法指定控制器:

$routes->get('/', [\App\Controllers\Home::class, 'index']);

或使用 use 关键字:

use App\Controllers\Home;

$routes->get('/', [Home::class, 'index']);

如果忘记添加 use App\Controllers\Home;,控制器类名将被解释为 \Home,而不是 App\Controllers\Home

备注

使用数组可调用语法时,类名始终被解释为完全限定的类名。因此 默认命名空间命名空间选项 不会生效。

数组可调用语法与占位符

如果存在占位符,将自动按指定顺序设置参数:

use App\Controllers\Product;

$routes->get('product/(:num)/(:num)', [Product::class, 'index']);

// The above code is the same as the following:
$routes->get('product/(:num)/(:num)', 'Product::index/$1/$2');

但是,如果在路由中使用正则表达式,自动配置的参数可能不正确。在这种情况下,可以手动指定参数:

use App\Controllers\Product;

$routes->get('product/(:num)/(:num)', [[Product::class, 'index'], '$2/$1']);

// The above code is the same as the following:
$routes->get('product/(:num)/(:num)', 'Product::index/$2/$1');

使用闭包

可以使用匿名函数(即闭包)作为路由映射的目标。当用户访问该 URI 时将执行此函数。这对于快速执行小任务或仅显示简单视图非常方便:

<?php

use App\Libraries\RSSFeeder;

$routes->get('feed', static function () {
    $rss = new RSSFeeder();

    return $rss->feed('general');
});

指定路由路径

占位符

典型路由如下所示:

<?php

$routes->get('product/(:num)', 'Catalog::productLookup');

在路由中,第一个参数是待匹配的 URI,第二个参数是目标路由。在上面的示例中,如果在 URL 路径的第一段找到 "product",并在第二段找到数字,则使用 Catalog 类和 productLookup 方法。

占位符只是表示正则表达式模式的字符串。在路由过程中,这些占位符被正则表达式的值替换。它们主要用于提高可读性。

以下是可用的占位符:

占位符

描述

(:any)

匹配从该位置到 URI 结尾的所有字符。可能包含多个 URI 段。

(:segment)

匹配除正斜杠(/)以外的任何字符,将结果限制为单个段。

(:num)

匹配任意正整数。

(:alpha)

匹配任意字母字符串

(:alphanum)

匹配任意字母数字字符串或整数,或两者的任意组合。

(:hash)

(:segment) 相同,但可用于轻松查看哪些路由使用哈希 ID。

备注

{locale} 不能用作占位符或路由的其他部分,因为它保留用于 本地化

(:any) 的行为

注意,单个 (:any) 将在 URL 中匹配多个段(如果存在)。

例如路由:

<?php

$routes->get('product/(:any)', 'Catalog::productLookup/$1');

将匹配 product/123product/123/456product/123/456/789 等。

默认情况下,在上面的示例中,如果 $1 占位符包含斜杠(/),在传递给 Catalog::productLookup() 时仍会被拆分为多个参数。

备注

自 v4.5.0 起,可以通过配置选项更改此行为。 详情请参阅 将多个 URI 分段作为一个参数

控制器中的实现应考虑最大参数数量:

<?php

namespace App\Controllers;

class Catalog extends BaseController
{
    public function productLookup($seg1 = false, $seg2 = false, $seg3 = false)
    {
        echo $seg1; // Will be 123 in all examples
        echo $seg2; // false in first, 456 in second and third example
        echo $seg3; // false in first and second, 789 in third
    }
}

或者可以使用 可变数量的参数值列表

<?php

namespace App\Controllers;

class Catalog extends BaseController
{
    public function productLookup(...$params)
    {
        echo $params[0] ?? null; // Will be 123 in all examples
        echo $params[1] ?? null; // null in first, 456 in second and third example
        echo $params[2] ?? null; // null in first and second, 789 in third
    }
}

重要

不要在 (:any) 之后放置任何占位符,因为传递给控制器方法的参数数量可能会改变。

如果匹配多个段不是预期的行为,在定义路由时应使用 (:segment)。使用上面的示例 URL:

<?php

$routes->get('product/(:segment)', 'Catalog::productLookup/$1');

将仅匹配 product/123,其他情况返回 404 错误。

自定义占位符

可以创建自己的占位符,用于路由文件以完全自定义体验和可读性。

使用 addPlaceholder() 方法添加新占位符。第一个参数是用作占位符的字符串,第二个参数是应替换它的正则表达式模式。需在添加路由前调用:

<?php

$routes->addPlaceholder('uuid', '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}');
$routes->get('users/(:uuid)', 'Users::show/$1');

正则表达式

如果愿意,可以使用正则表达式定义路由规则。允许任何有效的正则表达式,也支持反向引用。

重要

注意:如果使用反向引用,必须使用美元符号语法而不是双反斜杠语法。典型的正则表达式路由如下所示:

<?php

$routes->get('products/([a-z]+)/(\d+)', 'Products::show/$1/id_$2');

在上面的示例中,类似于 products/shirts/123 的 URI 将改为调用 Products 控制器类的 show() 方法,并将原始第一段和第二段作为参数传递给它。

使用正则表达式,还可以捕获包含正斜杠(/)的段,这通常代表多个段之间的分隔符。

例如,如果用户访问 Web 应用程序的密码保护区域,并且希望在登录后将其重定向回同一页面,可能会发现此示例很有用:

<?php

$routes->get('login/(.+)', 'Auth::login/$1');

默认情况下,在上面的示例中,如果 $1 占位符包含斜杠(/),在传递给 Auth::login() 时仍会被拆分为多个参数。

备注

自 v4.5.0 起,可以通过配置选项更改此行为。 详情请参阅 将多个 URI 分段作为一个参数

对于不了解正则表达式但想了解更多内容的人,regular-expressions.info 可能是一个不错的起点。

备注

还可以将占位符与正则表达式混合使用。

视图路由

Added in version 4.3.0.

如果只想渲染一个没有关联逻辑的视图,可以使用 view() 方法。此方法始终视为 GET 请求。该方法接受要加载的视图名称作为第二个参数。

<?php

// Displays the view in /app/Views/pages/about.php
$routes->view('about', 'pages/about');

如果在路由中使用占位符,可以在视图中的特殊变量 $segments 内访问它们。它们以数组形式可用,按它们在路由中出现的顺序索引。

<?php

// Displays the view in /app/Views/map.php
$routes->view('map/(:segment)/(:segment)', 'map');

// Within the view, you can access the segments with
// $segments[0] and $segments[1] respectively.

重定向路由

任何存在足够长时间的网站必然会有页面发生迁移。可以使用 addRedirect() 方法指定应重定向到其他路由的路由。第一个参数是旧路由的 URI 模式。第二个参数是要重定向到的新 URI 或命名路由的名称。第三个参数是应随重定向一起发送的 HTTP 状态码。默认值为 302,这是临时重定向,在大多数情况下推荐使用:

<?php

$routes->get('users/profile', 'Users::profile', ['as' => 'profile']);

// Redirect to a named route
$routes->addRedirect('users/about', 'profile');
// Redirect to a URI
$routes->addRedirect('users/about', 'users/profile');

// Redirect with placeholder
$routes->get('post/(:num)/comment/(:num)', 'PostsComments::index', ['as' => 'post.comment']);

// Redirect to a URI
$routes->addRedirect('article/(:num)/(:num)', 'post/$1/comment/$2');
// Redirect to a named route
$routes->addRedirect('article/(:num)/(:num)', 'post.comment');

备注

自 v4.2.0 起,addRedirect() 可以使用占位符。

如果在页面加载期间匹配到重定向路由,用户将立即重定向到新页面,然后才会加载控制器。

环境限制

可以创建仅在特定环境中可见的路由集。用于创建仅在开发人员本地机器上可用,而测试或生产服务器上无法访问的工具。可以使用 environment() 方法。第一个参数是环境名称。此闭包内定义的任何路由只能从给定环境访问:

<?php

$routes->environment('development', static function ($routes) {
    $routes->get('builder', 'Tools\Builder::index');
});

任意 HTTP 方法的路由

重要

此方法仅出于向后兼容而存在。不要在新项目中使用。即使已经在使用,也建议使用更合适的 HTTP 方法路由。

警告

虽然 add() 方法看起来很方便,但建议始终使用上述基于 HTTP 方法的路由,因为它更安全。如果使用 CSRF 保护,它不会保护 GET 请求。如果 add() 方法中指定的 URI 可以通过 GET 方法访问,CSRF 保护将无法工作。

使用 add() 方法定义支持任意 HTTP 方法的路由:

<?php

$routes->add('products', 'Product::feature');

备注

使用基于 HTTP 方法的路由还会提供轻微的性能提升,因为只存储与当前请求方法匹配的路由,从而在尝试查找匹配时需要扫描的路由更少。

批量映射路由

重要

此方法仅出于向后兼容而存在。不要在新项目中使用。即使已经在使用,也建议使用更合适的 HTTP 方法路由。

警告

map() 方法也不推荐使用,因为它在内部调用 add()

虽然 add() 方法简单易用,但使用 map() 方法同时处理多个路由通常更方便。可以定义路由数组,然后将其作为第一个参数传递给 map() 方法,而不是为每个需要添加的路由调用 add() 方法:

<?php

$multipleRoutes = [
    'product/(:num)'      => 'Catalog::productLookupById',
    'product/(:alphanum)' => 'Catalog::productLookupByName',
];

$routes->map($multipleRoutes);

仅命令行路由

备注

建议使用 Spark 命令处理 CLI 脚本,而不是通过 CLI 调用控制器。 有关详细信息,请参阅 创建 Spark 命令 页面。

通过 HTTP 方法创建的路由在 CLI 中不可访问,但由 add() 方法创建的路由仍可从命令行使用。

可以使用 cli() 方法创建仅在命令行中工作,且无法从 Web 浏览器访问的路由:

<?php

$routes->cli('migrate', 'App\Database::migrate');

警告

如果启用 自动路由(传统版) 并将命令文件放在 app/Controllers 中,任何人都可以通过自动路由(传统)通过 HTTP 访问该命令。

全局选项

创建路由的所有方法(get()post()resource() 等)都可以接受一个选项数组,这些选项可以修改生成的路由或进一步限制它们。$options 数组始终是最后一个参数:

<?php

$routes->add('from', 'to', $options);
$routes->get('from', 'to', $options);
$routes->post('from', 'to', $options);
$routes->put('from', 'to', $options);
$routes->head('from', 'to', $options);
$routes->options('from', 'to', $options);
$routes->delete('from', 'to', $options);
$routes->patch('from', 'to', $options);
$routes->match(['GET', 'PUT'], 'from', 'to', $options);
$routes->resource('photos', $options);
$routes->map($array, $options);
$routes->group('name', $options, static function () {});

应用过滤器

可以通过提供在控制器之前或之后运行的过滤器,来修改特定路由的行为。这在身份验证或 API 日志记录期间特别方便。

过滤器的值可以是字符串或字符串数组:

  • 匹配 app/Config/Filters.php 中定义的别名

  • 过滤器类名

有关定义别名的更多信息,请参阅 控制器过滤器

警告

如果在 app/Config/Routes.php 中设置路由的过滤器(而不是在 app/Config/Filters.php 中),建议禁用自动路由(传统)。启用 自动路由(传统版) 时,控制器可能通过与配置的路由不同的 URL 访问,在这种情况下,指定给路由的过滤器将不会应用。请参阅 仅使用已定义的路由 以禁用自动路由。

别名过滤器

为过滤器值指定 在 app/Config/Filters.php 中定义 的别名:

<?php

$routes->get('admin', ' AdminController::index', ['filter' => 'admin-auth']);

还可以提供要传递给别名过滤器的 before()after() 方法的参数:

<?php

$routes->post('users/delete/(:segment)', 'AdminController::index', ['filter' => 'admin-auth:dual,noreturn']);

类名过滤器

Added in version 4.1.5.

可以为过滤器值指定过滤器类名:

<?php

$routes->get('admin', ' AdminController::index', ['filter' => \App\Filters\SomeFilter::class]);

多重过滤器

Added in version 4.1.5.

重要

自 v4.5.0 起,多重过滤器 始终处于启用状态。在 v4.5.0 之前,多重过滤器 默认禁用。如果要在 v4.5.0 之前使用,请参阅 从 4.1.4 升级到 4.1.5 了解详情。

可以为过滤器值指定数组:

<?php

$routes->get('admin', ' AdminController::index', ['filter' => ['admin-auth', \App\Filters\SomeFilter::class]]);
过滤器参数

可以向过滤器传递额外的参数:

<?php

$routes->add('users/delete/(:segment)', 'AdminController::index', ['filter' => 'admin-auth:dual,noreturn']);

在此示例中,数组 ['dual', 'noreturn'] 将作为 $arguments 传递给过滤器的 before()after() 方法。

分配命名空间

虽然系统会自动添加 默认命名空间 到生成的控制器,但可通过 namespace 选项指定不同命名空间:

<?php

// Routes to \Admin\Users::index()
$routes->get('admin/users', 'Users::index', ['namespace' => 'Admin']);

新命名空间仅作用于单路由创建方法(如 get、post 等)。对于创建多路由的方法(如 group()),新命名空间将附加到所有生成的路由。

限制主机名

通过“hostname”选项限制路由组仅在特定域名或子域生效:

<?php

$routes->get('from', 'to', ['hostname' => 'accounts.example.com']);

此示例仅允许 accounts.example.com 域名访问,主域 example.com 不可用。

多主机名限制

Added in version 4.6.0.

也可以限制多个主机名,例如:

<?php

$routes->get('from', 'to', ['hostname' => ['s1.example.com', 's2.example.com']]);

限制子域

当存在 subdomain 选项时,系统将限制路由仅在该子域上可用。只有当子域是查看应用程序所使用的子域时,路由才会匹配:

<?php

// Limit to media.example.com
$routes->get('from', 'to', ['subdomain' => 'media']);

设置值为星号(*)可匹配任意子域,但无子域的 URL 不匹配:

<?php

// Limit to any sub-domain
$routes->get('from', 'to', ['subdomain' => '*']);

重要

此功能并非完美,在生产环境中使用之前应对特定域进行测试。大多数域应该可以正常工作,但一些边缘情况,特别是包含句点的域名(不用于分隔后缀或 www)可能导致误报。

偏移匹配的参数

可以使用 offset 选项通过任何数值偏移路由中的匹配参数,该值是偏移的段数。

这在开发 API 时可能很有益,因为第一段 URI 是版本号。当第一参数是语言字符串时也可以使用:

<?php

$routes->get('users/(:num)', 'users/show/$1', ['offset' => 1]);

// Creates:
$routes['users/(:num)'] = 'users/show/$2';

反向路由

反向路由允许通过控制器、方法及参数定义链接,并让路由器查找当前的路由。这使得修改路由定义无需更新应用代码。常用于在视图中创建链接。

例如,如果有一个想要链接到的照片库的路由,可以使用 url_to() 辅助函数获取应使用的路由。第一个参数是完全限定的控制器和方法,用双冒号(::)分隔,就像编写初始路由本身一样。剩下的参数会传递给路由:

<?php

// The route is defined as:
$routes->get('users/(:num)/gallery/(:num)', 'Galleries::showUserGallery/$1/$2');

?>

<!-- Generate the URI to link to user ID 15, gallery 12: -->
<a href="<?= url_to('Galleries::showUserGallery', 15, 12) ?>">View Gallery</a>
<!-- Result: 'http://example.com/users/15/gallery/12' -->

命名路由

可以为路由命名以使应用程序更不易出错。这为路由分配了一个名称,可以稍后调用,即使路由定义更改,应用程序中使用 url_to() 构建的所有链接仍将工作,而无需进行任何更改。通过传入带有路由名称的 as 选项来命名路由:

<?php

// The route is defined as:
$routes->get('users/(:num)/gallery/(:num)', 'Galleries::showUserGallery/$1/$2', ['as' => 'user_gallery']);

?>

<!-- Generate the URI to link to user ID 15, gallery 12: -->
<a href="<?= url_to('user_gallery', 15, 12) ?>">View Gallery</a>
<!-- Result: 'http://example.com/users/15/gallery/12' -->

这样做还具有使视图更易读的附加好处。

备注

默认情况下,所有定义的路由的名称都与其路径匹配,占位符替换为相应的正则表达式。例如,如果定义路由如 $routes->get('edit/(:num)', 'PostController::edit/$1');,可以使用 route_to('edit/([0-9]+)', 12) 生成相应的 URL。

警告

根据 路由优先级,如果首先定义了一个未命名的路由(例如 $routes->get('edit', 'PostController::edit');),然后定义了另一个与第一个路由路径相同名称的路由(例如 $routes->get('edit/(:num)', 'PostController::edit/$1', ['as' => 'edit']);),第二个路由将不会被注册,因为它的名称会与第一个路由自动分配的名称冲突。

分组路由

可以使用 group() 方法将路由分组到一个共用名称下。该组名称会成为一个 URI 段,显示在该组内定义的路由之前。这使你能够减少创建大量共享相同起始字符串的路由所需的输入,例如在构建管理后台时:

<?php

$routes->group('admin', static function ($routes) {
    $routes->get('users', 'Admin\Users::index');
    $routes->get('blog', 'Admin\Blog::index');
});

这将为 usersblog URI 添加 admin 前缀,处理如 admin/usersadmin/blog 的 URL。

设置命名空间

如果需要为组分配选项,如 分配命名空间,请在回调之前执行:

<?php

$routes->group('api', ['namespace' => 'App\API\v1'], static function ($routes) {
    $routes->resource('users');
});

这将处理到 App\API\v1\Users 控制器的资源路由,URI 为 api/users

设置过滤器

你也可以为一组路由使用特定的 过滤器。过滤器总是在控制器之前或之后运行。这个功能在身份验证或 API 日志记录时特别方便:

<?php

$routes->group('api', ['filter' => 'api-auth'], static function ($routes) {
    $routes->resource('users');
});

过滤器的值必须与 app/Config/Filters.php 中定义的别名之一匹配。

备注

在 v4.5.4 之前,由于一个 Bug,传递给 group() 的过滤器不会合并到传递给内部路由的过滤器中。

设置其他选项

在某些情况下,你可能希望把一些路由分组,以便统一应用过滤器,或其他路由配置选项(如命名空间、子域名等)。 如果你并不需要为这个分组添加 URI 前缀,可以在定义分组时将前缀设为空字符串。这样,分组中的路由在访问路径上看起来就像这个分组不存在一样,但仍然会应用该分组中设置的路由配置选项:

<?php

$routes->group('', ['namespace' => 'Myth\Auth\Controllers'], static function ($routes) {
    $routes->get('login', 'AuthController::login', ['as' => 'login']);
    $routes->post('login', 'AuthController::attemptLogin');
    $routes->get('logout', 'AuthController::logout');
});

嵌套分组

支持多层级分组以实现更精细的组织结构:

<?php

$routes->group('admin', ['filter' => 'myfilter1:config'], static function ($routes) {
    $routes->get('/', 'Admin\Admin::index');

    $routes->group('users', ['filter' => 'myfilter2:region'], static function ($routes) {
        $routes->get('list', 'Admin\Users::list');
    });
});

这段配置将会处理 admin/users/list 这个 URL。

传递给外层 group()filter 选项,会与内层 group() 中的 filter 选项合并生效。 因此,上面的代码中,对于 admin 这个路由,只会执行 myfilter1:config。对于 admin/users/list 这个路由,则会同时执行 myfilter1:configmyfilter2:region

备注

在 v4.6.0 之前,同一个过滤器不能以不同的参数被多次执行。

对于其他存在重叠的配置项,如果在内层 group() 中传入了新的值,那么这些值会覆盖外层 group() 中对应的设置。

备注

在 v4.5.0 之前,由于一个 Bug,外层 group()` 中传入的选项不会与内层 group()` 的选项进行合并。

路由优先级

路由按照定义的顺序注册到路由表中。这意味着访问 URI 时,将执行第一个匹配的路由。

警告

如果路由路径被多次定义且处理程序不同,仅首个定义的路由生效。

可以通过运行 spark routes 命令来检查路由表中注册的路由。

调整路由优先级

在使用模块时,如果应用程序中的路由包含通配符,可能会出现问题。 然后模块路由将无法正确处理。 你可以通过使用 priority 选项降低路由处理的优先级来解决此问题。 该参数接受正整数和零。在 priority 中指定的数字越大,处理队列中的路由优先级越低:

<?php

// First you need to enable processing of the routes queue by priority.
$routes->setPrioritize();

// Config\Routes
$routes->get('(.*)', 'Posts::index', ['priority' => 1]);

// Modules\Acme\Config\Routes
$routes->get('admin', 'Admin::index');

// The "admin" route will now be processed before the wildcard route.

要禁用此功能,必须使用参数 false 调用该方法:

<?php

$routes->setPrioritize(false);

备注

默认情况下,所有路由的优先级均为 0。 负整数将强制转换为绝对值。

路由配置选项

RoutesCollection 类提供了几个影响所有路由的选项,可以进行修改以满足你的应用程序的需求。 这些选项在 app/Config/Routing.php 中可用。

备注

配置文件 app/Config/Routing.php 自 v4.4.0 起添加。 在以前的版本中,在 app/Config/Routes.php 中使用 setter 方法来更改设置。

默认命名空间

将控制器与路由匹配时,系统会将默认命名空间值添加到控制器名称前面。 默认情况下,此值为 App\Controllers

如果将值设置为空字符串(''),则让每个路由指定完整命名空间的控制器:

<?php

// In app/Config/Routing.php
use CodeIgniter\Config\Routing as BaseRouting;

// ...
class Routing extends BaseRouting
{
    // ...
    public string $defaultNamespace = '';
    // ...
}

// In app/Config/Routes.php
// Controller is \Users
$routes->get('users', 'Users::index');

// Controller is \Admin\Users
$routes->get('users', 'Admin\Users::index');

如果你的控制器未显式使用命名空间,则无需更改此项。 如果你为控制器使用了命名空间,则可以更改此值以减少输入:

<?php

// This can be overridden in app/Config/Routes.php
$routes->setDefaultNamespace('App');

// Controller is \App\Users
$routes->get('users', 'Users::index');

// Controller is \App\Admin\Users
$routes->get('users', 'Admin\Users::index');

默认方法

当路由处理程序只有控制器名称而没有列出方法名称时,使用此设置。 默认值为 index

// In app/Config/Routing.php
public string $defaultMethod = 'index';

备注

$defaultMethod 也与自动路由通用。 请参阅 自动路由(改进版)自动路由(传统版)

如果你定义以下路由:

$routes->get('/', 'Home');

当路由匹配时,将执行 App\Controllers\Home 控制器的 index() 方法。

备注

不能使用以 _ 开头的方法名称作为默认方法。 但是,从 v4.5.0 开始,允许使用 __invoke 方法。

转换 URI 中的短横线

该选项允许在使用自动路由时,自动将 URI 中的短横线(-)替换为控制器和方法名中的下划线,从而减少额外的路由定义。 这是因为短横线不是合法的类名或方法名字符,直接使用会导致致命错误:

<?php

// In app/Config/Routing.php
use CodeIgniter\Config\Routing as BaseRouting;

// ...
class Routing extends BaseRouting
{
    // ...
    public bool $translateURIDashes = true;
    // ...
}

// This can be overridden in app/Config/Routes.php
$routes->setTranslateURIDashes(true);

备注

使用自动路由(改进版)时,在 v4.4.0 之前,如果 $translateURIDashes 为 true,带短横线(如 foo-bar)和带下划线(如 foo_bar) 的两个 URI 会指向同一个控制器方法,这是不正确的行为。 自 v4.4.0 起,带下划线的 URI(foo_bar)将无法访问。

仅使用已定义的路由

自 v4.2.0 起,自动路由默认被禁用。

当没有找到匹配的定义路由时,如果启用了自动路由,系统会尝试根据控制器和方法名进行匹配。

可以通过将 $autoRoute 设为 false,禁用这种自动匹配,从而仅允许访问已定义的路由:

<?php

// In app/Config/Routing.php
use CodeIgniter\Config\Routing as BaseRouting;

// ...
class Routing extends BaseRouting
{
    // ...
    public bool $autoRoute = false;
    // ...
}

// This can be overridden in app/Config/Routes.php
$routes->setAutoRoute(false);

警告

如果使用 CSRF 保护,它不会保护 GET 请求。如果某个 URI 可通过 GET 方法访问,CSRF 保护将不会生效。

404 重写

当找不到与当前 URI 匹配的页面时,系统将显示一个通用的 404 视图。通过在路由配置文件中使用 $override404 属性,你可以为 404 路由定义控制器类/方法。

<?php

// In app/Config/Routing.php
use CodeIgniter\Config\Routing as BaseRouting;

// ...
class Routing extends BaseRouting
{
    // ...
    public ?string $override404 = 'App\Errors::show404';
    // ...
}

你还可以在路由配置文件中使用 set404Override() 方法指定在发生 404 错误时执行的操作。该值可以是一个有效的类/方法对,或者是一个闭包:

<?php

// In app/Config/Routes.php
// Would execute the show404 method of the App\Errors class
$routes->set404Override('App\Errors::show404');

// Will display a custom view.
$routes->set404Override(static function () {
    // If you want to get the URI segments.
    $segments = request()->getUri()->getSegments();

    return view('my_errors/not_found.html');
});

备注

从 v4.5.0 开始,404 覆盖功能默认将响应状态码设置为 404。 在以前的版本中,状态码为 200。 如果要在控制器中更改状态码,请参阅 CodeIgniter\HTTP\Response::setStatusCode() 了解如何设置状态码。

按优先级处理路由

用于启用或禁用按优先级处理路由队列。降低优先级通过路由选项进行设置。 该功能默认禁用,并会影响所有路由。 降低优先级的示例请参见 路由优先级

<?php

// In app/Config/Routing.php
use CodeIgniter\Config\Routing as BaseRouting;

// ...
class Routing extends BaseRouting
{
    // ...
    public bool $prioritize = true;
    // ...
}

// In app/Config/Routes.php
// to enable
$routes->setPrioritize();

// to disable
$routes->setPrioritize(false);

将多个 URI 分段作为一个参数

Added in version 4.5.0.

启用此选项后,匹配多个段的占位符(例如 (:any))将直接作为一个参数传递,即使它包含多个段。

<?php

// In app/Config/Routing.php
use CodeIgniter\Config\Routing as BaseRouting;

class Routing extends BaseRouting
{
    // ...
    public bool $multipleSegmentsOneParam = true;
    // ...
}

例如以下路由:

<?php

$routes->get('product/(:any)', 'Catalog::productLookup/$1');

将匹配 product/123product/123/456product/123/456/789 等。 如果 URI 是 product/123/456,则 123/456 会作为第一个参数传递给 Catalog::productLookup() 方法。

自动路由(改进版)

Added in version 4.2.0.

自动路由(改进版)是一套新的、更安全的自动路由系统。

有关详细信息,请参阅 自动路由(改进版)

自动路由(传统版)

重要

该功能仅用于向后兼容。不要在新项目中使用。 即使已经在使用,也强烈建议改用 自动路由(改进版)

自动路由(传统版)是 CodeIgniter 3 的路由系统。 可根据约定自动将 HTTP 请求路由到控制器方法。

建议在 app/Config/Routes.php 文件中定义所有路由,或使用 自动路由(改进版)

警告

为了防止配置错误和编码失误,强烈不建议使用自动路由(传统版)。它很容易导致控制器过滤器或 CSRF 保护被绕过,从而产生安全漏洞。

重要

自动路由(传统版)会将 任意 HTTP 方法的请求都路由到控制器方法。

启用自动路由(传统版)

自 v4.2.0 起,默认情况下禁用自动路由。

要启用该功能,需要在 app/Config/Routing.php 中将 $autoRoute 设为 true:

public bool $autoRoute = true;

并在 app/Config/Feature.php 中将 $autoRoutesImproved 设为 false:

public bool $autoRoutesImproved = false;

URI 分段(传统版)

在遵循 MVC 模式时,URL 中的分段通常表示为:

example.com/class/method/ID
  1. 第一段表示要调用的控制器

  2. 第二段表示要调用的控制器 方法

  3. 第三段及后续段表示传递给控制器的 ID 或其他变量。

例如以下 URI:

example.com/index.php/helloworld/index/1

在该示例中,CodeIgniter 会尝试找到名为 Helloworld.php 的控制器,并执行 index() 方法,同时将 '1' 作为第一个参数传入。

有关更多信息,请参阅 控制器中的自动路由(传统版)

配置选项(传统版)

这些选项定义在 app/Config/Routing.php 中。

默认控制器(传统版)

站点根 URI(传统版)

当用户访问站点根目录(如 example.com)时,如果未显式定义路由,将使用 $defaultController 属性指定的控制器。

默认值为 Home,对应控制器文件 app/Controllers/Home.php:

public string $defaultController = 'Home';
目录 URI(传统版)

当未找到匹配路由,且 URI 指向控制器目录中的某个子目录时,也会使用默认控制器。 例如访问 example.com/admin,如果存在 app/Controllers/Admin/Home.php,则会使用该控制器。

有关更多信息,请参阅 控制器中的自动路由(传统版)

默认方法(传统版)

该设置与默认控制器类似,用于在找到匹配控制器、但 URI 中未指定方法段时,确定要调用的默认方法。默认值为 index

在下面的示例中,如果用户访问 example.com/products,且存在 Products 控制器,则会执行 Products::listAll() 方法:

public string $defaultMethod = 'listAll';

验证路由

CodeIgniter 提供了以下 命令 用于显示所有路由。

spark 路由

显示所有路由和过滤器:

php spark routes

输出示例如下:

+---------+---------+---------------+-------------------------------+----------------+---------------+
| Method  | Route   | Name          | Handler                       | Before Filters | After Filters |
+---------+---------+---------------+-------------------------------+----------------+---------------+
| GET     | /       | »             | \App\Controllers\Home::index  |                | toolbar       |
| GET     | feed    | »             | (Closure)                     |                | toolbar       |
+---------+---------+---------------+-------------------------------+----------------+---------------+

Method 列表示路由监听的 HTTP 方法。

Route 列表示要匹配的路由路径。定义路由的路径会以正则表达式形式显示。

自 v4.3.0 起,Name 列显示路由名称。» 表示路由名称与路径相同。

重要

该系统并非完美。对于包含正则表达式(如 ([^/]+){locale})的路由,如果在 app/Config/Filters.php 中为过滤器设置了复杂的 URI 模式,显示的 Filters 可能不正确,或显示为 <unknown>

可以使用 spark filter:check 命令检查 100% 准确的过滤器信息。

自动路由(改进版)

使用自动路由(改进版)时,输出示例如下:

+-----------+-------------------------+---------------+-----------------------------------+----------------+---------------+
| Method    | Route                   | Name          | Handler                           | Before Filters | After Filters |
+-----------+-------------------------+---------------+-----------------------------------+----------------+---------------+
| GET(auto) | product/list/../..[/..] |               | \App\Controllers\Product::getList |                | toolbar       |
+-----------+-------------------------+---------------+-----------------------------------+----------------+---------------+

Method 列会显示为 GET(auto)

Route 列中的 /.. 表示一个分段。[/..] 表示可选分段。

备注

启用自动路由时,如果存在 home` 路由,也可以通过 HomehOmehoMeHOME` 等方式访问,但命令输出中只会显示 home

如果看到以 x 开头的路由,表示该路由无效,不会被路由,但控制器中存在可路由的 public 方法。

+-----------+----------------+------+-------------------------------------+----------------+---------------+
| Method    | Route          | Name | Handler                             | Before Filters | After Filters |
+-----------+----------------+------+-------------------------------------+----------------+---------------+
| GET(auto) | x home/foo     |      | \App\Controllers\Home::getFoo       | <unknown>      | <unknown>     |
+-----------+----------------+------+-------------------------------------+----------------+---------------+

该示例表示存在 \App\Controllers\Home::getFoo() 方法,但由于它是默认控制器(默认为 Home),默认控制器名称不能出现在 URI 中,因此不会被路由。应删除 getFoo() 方法。

备注

在 v4.3.4 之前,由于一个 Bug,无效路由会被显示为普通路由。

自动路由(传统版)

使用自动路由(传统版)时,输出示例如下:

+--------+--------------------+---------------+-----------------------------------+----------------+---------------+
| Method | Route              | Name          | Handler                           | Before Filters | After Filters |
+--------+--------------------+---------------+-----------------------------------+----------------+---------------+
| auto   | product/list[/...] |               | \App\Controllers\Product::getList |                | toolbar       |
+--------+--------------------+---------------+-----------------------------------+----------------+---------------+

Method 列会显示为 auto

Route 列中的 [/...] 表示任意数量的分段。

备注

启用自动路由时,如果存在 home 路由,也可以通过 HomehOmehoMeHOME` 等方式访问,但命令输出中只会显示 home

按处理程序排序

Added in version 4.3.0.

可以按 Handler 对路由进行排序:

php spark routes -h

指定主机

Added in version 4.4.0.

可以使用 --host 选项,在请求 URL 中指定主机名:

php spark routes --host accounts.example.com

获取路由信息

在 CodeIgniter 4 中,理解并管理路由信息对于高效处理 HTTP 请求至关重要。 这包括获取当前控制器和方法,以及应用于特定路由的过滤器信息。 下面将介绍如何访问这些路由信息,以辅助日志记录、调试或实现条件逻辑等任务。

获取当前控制器 / 方法名称

在某些情况下,可能需要确定当前 HTTP 请求触发了哪个控制器和方法。 这对于日志记录、调试,或基于当前控制器方法实现条件逻辑非常有用。

CodeIgniter 4 提供了 Router 类,可以方便地获取当前路由的控制器和方法名称,示例如下:

<?php

// Get the router instance.
/** @var \CodeIgniter\Router\Router $router */
$router = service('router');

// Retrieve the fully qualified class name of the controller handling the current request.
$controller = $router->controllerName();

// Retrieve the method name being executed in the controller for the current request.
$method = $router->methodName();

echo 'Current Controller: ' . $controller . '<br>';
echo 'Current Method: ' . $method;

当需要动态与控制器交互,或记录处理请求的方法时,该功能尤其有用。

获取当前路由的活动过滤器

过滤器 是一项强大的功能,可在处理 HTTP 请求之前或之后执行身份验证、日志记录和安全检查等操作。要获取当前路由的活动过滤器,可以使用 Router 类中的 CodeIgniter\Router\Router::getFilters() 方法。

该方法会返回当前路由正在使用的过滤器列表:

<?php

// Get the router instance.
/** @var \CodeIgniter\Router\Router $router */
$router  = service('router');
$filters = $router->getFilters();

echo 'Active Filters for the Route: ' . implode(', ', $filters);

备注

getFilters()` 方法仅返回为该特定路由定义的过滤器,不包含全局过滤器,也不包含 app/Config/Filters.php 中定义的过滤器。

获取当前路由匹配的选项

在定义路由时,可以设置一些可选参数:filter`namespacehostnamesubdomainoffsetpriorityas。这些内容在前文中已有说明。此外,如果使用了 addRedirect(),还可能包含 redirect 键。 要访问这些参数的值,可以调用 Router::getMatchedRouteOptions()。以下是返回数组的示例:

<?php

// Get the router instance.
/** @var \CodeIgniter\Router\Router $router */
$router  = service('router');
$options = $router->getMatchedRouteOptions();

echo 'Route name: ' . $options['as'];

print_r($options);

// Route name: api:auth
//
// Array
// (
//     [filter] => api-auth
//     [namespace] => App\API\v1
//     [hostname] => example.com
//     [subdomain] => api
//     [offset] => 1
//     [priority] => 1
//     [as] => api:auth
// )