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) |
与 |
备注
{locale} 不能用作占位符或路由的其他部分,因为它保留用于 本地化。
(:any) 的行为
注意,单个 (:any) 将在 URL 中匹配多个段(如果存在)。
例如路由:
<?php
$routes->get('product/(:any)', 'Catalog::productLookup/$1');
将匹配 product/123、product/123/456、product/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');
});
这将为 users 和 blog URI 添加 admin 前缀,处理如 admin/users 和 admin/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:config 和 myfilter2: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';
如果你定义以下路由:
$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/123、product/123/456、product/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
第一段表示要调用的控制器 类。
第二段表示要调用的控制器 方法。
第三段及后续段表示传递给控制器的 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` 路由,也可以通过 Home、hOme、hoMe、HOME` 等方式访问,但命令输出中只会显示 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 路由,也可以通过 Home、hOme、hoMe、HOME` 等方式访问,但命令输出中只会显示 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、`namespace、hostname、subdomain、offset、priority、as。这些内容在前文中已有说明。此外,如果使用了 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
// )