自动路由(改进版)
Added in version 4.2.0.
什么是自动路由(改进版)?
默认情况下,所有路由都必须在配置文件中 定义。
但是,使用 自动路由(改进版),你可以根据约定定义控制器名称及其方法名称,它将被自动路由。换句话说,无需手动定义路由。
如果启用自动路由(改进版),当找不到与 URI 匹配的已定义路由时,系统将尝试根据该 URI 匹配控制器和方法。
重要
出于安全考虑,如果一个控制器已在定义的路由中被使用,那么自动路由(改进版)将不会再路由到该控制器。
备注
自动路由(改进版)默认禁用。要使用它,请参见 启用自动路由(改进版)。
与自动路由(传统版)的区别
自动路由(传统版) 是 CodeIgniter 3 中的路由系统。如果你不熟悉它,可以跳到下一节。
如果你很了解它,以下是 自动路由(改进版) 的一些变化:
- 控制器方法需要带有 HTTP 动词前缀,如
getIndex()、postCreate()。 由于开发者总是明确知道 HTTP 方法,因此使用预期之外的 HTTP 方法发出的请求将永远不会执行到该控制器。
- 控制器方法需要带有 HTTP 动词前缀,如
- URI 中必须省略默认控制器(默认为
Home)和默认方法(默认为index)。 这保证了控制器方法与 URI 的一一对应关系。
例如,默认情况下,你可以访问
/,但/home和/home/index将返回 404 错误。
- URI 中必须省略默认控制器(默认为
- 检查方法参数数量。
如果 URI 中的参数多于方法参数,将返回 404 错误。
- 不支持
_remap()方法。 这保证了控制器方法与 URI 的一一对应关系。
但提供了 默认方法回退 功能作为替代。
- 不支持
- 无法访问已在“定义路由”中配置的控制器。
这将通过 自动路由 访问的控制器与通过 定义路由 访问的控制器完全分离开来。
启用自动路由(改进版)
要使用该功能,需在 app/Config/Routing.php 中将 $autoRoute 选项设为 true:
public bool $autoRoute = true;
同时需在 app/Config/Feature.php 中将 $autoRoutesImproved 属性设为 true:
public bool $autoRoutesImproved = true;
重要
当你使用自动路由(改进版)时,必须删除
app/Config/Routes.php 中的 $routes->get('/', 'Home::index');。因为
已定义路由优先于自动路由,出于安全原因,已定义路由中定义的控制器会被自动路由(改进版)拒绝访问。
URI 分段
遵循 MVC 模式,URL 中的分段通常表示:
http://example.com/{class}/{method}/{param1}
第一分段表示要调用的控制器 类。
第二分段表示要调用的类 方法。
第三及后续分段表示传递给控制器方法的 参数。
示例 URI:
http://example.com/hello-world/hello/1
当使用 GET 方法发送 HTTP 请求时,自动路由(改进版)会尝试查找名为 App\Controllers\HelloWorld 的控制器,并执行 getHello() 方法,同时传递 '1' 作为第一个参数。
备注
通过自动路由(改进版)执行的控制器方法需要添加 HTTP 动词(get、post、put 等)前缀,如 getIndex()、postCreate()。
备注
当控制器的短名称与 URI 的第一分段匹配时,该控制器将被加载。
实践演练:Hello World!
让我们创建一个简单控制器来演示该功能。
创建控制器
使用你的文本编辑器,在 app/Controllers 目录中创建一个名为 HelloWorld.php 的文件,并将以下代码放入其中。
<?php
namespace App\Controllers;
class HelloWorld extends BaseController
{
public function getIndex()
{
return 'Hello World!';
}
}
重要
文件名必须为 HelloWorld.php。使用自动路由(改进版)时,控制器类名必须采用大驼峰式命名法。
注意 HelloWorld 控制器继承自 BaseController。若不需要 BaseController 的功能,也可直接继承 CodeIgniter\Controller。
BaseController 提供了方便的组件加载方式,并包含所有控制器共用的功能。你可以在任何新控制器中继承此类。
重要
通过自动路由(改进版)执行的控制器方法需要添加 HTTP 动词(get、post、put 等)前缀,如 getIndex()、postCreate()。
检查路由
可通过 spark routes 命令查看路由信息:
php spark routes
成功操作后将显示:
+-----------+-------------+------+---------------------------------------+----------------+---------------+
| Method | Route | Name | Handler | Before Filters | After Filters |
+-----------+-------------+------+---------------------------------------+----------------+---------------+
| GET(auto) | hello-world | | \App\Controllers\HelloWorld::getIndex | | |
+-----------+-------------+------+---------------------------------------+----------------+---------------+
详情请参阅 spark 路由。
访问你的站点
现在使用类似这样的 URL 访问你的站点:
http://example.com/hello-world
系统会自动将 URI 中的减号(-)转换为控制器和方法的大驼峰式命名。
例如,URI sub-dir/hello-controller/some-method 将执行 SubDir\HelloController::getSomeMethod() 方法。
成功操作后将显示:
Hello World!
控制器命名示例
以下为有效控制器名称,因为 App\Controllers\HelloWorld 采用大驼峰式命名:
<?php
namespace App\Controllers;
class HelloWorld extends BaseController
{
// ...
}
以下为 无效命名。因为首字母(h)不是大写。
<?php
namespace App\Controllers;
class helloworld extends BaseController
{
// ...
}
以下同样是 无效命名。因为首字母(h)不是大写。
<?php
namespace App\Controllers;
class helloWorld extends BaseController
{
// ...
}
控制器方法
方法可见性
当你定义一个可通过 HTTP 请求执行的方法时,该方法必须声明为 public。
警告
出于安全考虑,请将所有工具方法声明为 protected 或 private。
默认方法
在上面的示例中,方法名是 getIndex()。该方法(HTTP 动词 + Index())被称为 默认方法,当 URI 的 第二分段 为空时加载。
常规方法
URI 的第二分段决定调用控制器中的哪个方法。
让我们试试。向你的控制器添加一个新方法:
<?php
namespace App\Controllers;
class HelloWorld extends BaseController
{
public function getIndex()
{
return 'Hello World!';
}
public function getComment()
{
return 'I am not flat!';
}
}
现在访问以下 URL 来查看 getComment() 方法:
http://example.com/hello-world/comment/
你将看到新消息。
向方法传递 URI 分段
如果你的 URI 包含超过两个分段,它们将作为参数传递给方法。
例如,假设你有这样一个 URI:
http://example.com/products/shoes/sandals/123
该方法将接收第 3、4 分段('sandals' 和 '123'):
<?php
namespace App\Controllers;
class Products extends BaseController
{
public function getShoes($type, $id)
{
return $type . $id;
}
}
备注
如果 URI 中的参数多于方法参数,自动路由(改进版)不会执行该方法,并返回 404 错误。
默认控制器
默认控制器 是在 URI 以目录名结尾或未指定 URI(如访问站点根 URL)时使用的特殊控制器。
默认情况下,默认控制器是 Home。
备注
只在默认控制器中定义默认方法(对于 GET 请求是 getIndex())。
如果你定义任何其他公共方法,该方法将不会被执行。
有关更多信息,请参阅 配置选项。
默认方法回退
Added in version 4.4.0.
如果 URI 中代表方法名的分段所对应的控制器方法不存在,并且(在该控制器中)定义了默认方法,那么剩余的 URI 分段将会被传递给这个默认方法来执行。
<?php
namespace App\Controllers;
class Product extends BaseController
{
public function getIndex($id = null, $action = '')
{
// ...
}
}
访问以下 URL:
http://example.com/product/15/edit
该方法将接收第 2、3 分段('15' 和 'edit'):
重要
如果 URI 中的参数多于方法参数, 自动路由(改进版)不会执行该方法,并返回 404 错误。
回退到默认控制器
如果 URI 中代表控制器名的分段所对应的控制器不存在,并且该目录中存在默认控制器(默认为 Home),那么剩余的 URI 分段将会被传递给该默认控制器的默认方法。
例如,当你在 app/Controllers/News 目录中有以下默认控制器 Home 时:
<?php
namespace App\Controllers\News;
use App\Controllers\BaseController;
class Home extends BaseController
{
public function getIndex($id = null)
{
// ...
}
}
访问以下 URL:
http://example.com/news/101
将找到 News\Home 控制器和默认的 getIndex() 方法。因此默认方法将获取第二 URI 分段('101'):
备注
如果存在 App\Controllers\News 控制器,则优先使用。
URI 段按顺序搜索,使用找到的第一个控制器。
备注
如果 URI 中的参数多于方法参数, 自动路由(改进版)不会执行该方法,并返回 404 错误。
将控制器组织到子目录中
在构建大型应用程序时,你可能希望将控制器按层级结构放入子目录中来管理。CodeIgniter 支持这种做法。
你只需在 app/Controllers 主目录下创建子目录,然后将相应的控制器类(文件)放入这些目录中即可。
重要
目录名必须以大写字母开头,并且是大驼峰式命名。
使用此功能时,URI 的第一分段必须指定目录。例如,假设你有一个位于以下位置的控制器:
app/Controllers/Products/Shoes.php
要调用上述控制器,你的 URI 看起来会是这个样子:
http://example.com/products/shoes/show/123
备注
app/Controllers 和 public 目录不能存在同名目录,否则 Web 服务器将直接访问该目录而不会路由到 CodeIgniter。
每个子目录都可以包含一个默认控制器。如果 URL 访问的路径 刚好 是这个子目录(即没有指定具体的控制器名),那么这个默认控制器就会被调用。实现方法很简单:你只需在该目录中放置一个控制器,确保它的名称与你在 app/Config/Routing.php 文件中指定的默认控制器名称一致即可。
控制器/方法与 URI 对应示例
在默认配置下,当收到一个 GET 请求时,控制器/方法与 URI 之间的对应关系如下:
控制器/方法 |
URI |
说明 |
|---|---|---|
|
/ |
默认控制器和默认方法。 |
|
/blog |
默认方法。 |
|
/user-profile |
默认方法。 |
|
/blog/tags |
|
|
/blog/news/123 |
|
|
/blog |
|
|
/blog/tags |
|
|
/blog/news/123 |
|
应用过滤器
应用控制器过滤器可在控制器方法执行前后添加处理逻辑,适用于身份验证或 API 日志记录等场景。
使用自动路由时,需在 app/Config/Filters.php 中设置要应用的过滤器。 有关设置过滤器的更多信息,请参阅 控制器过滤器。
配置选项
以下选项位于 app/Config/Routing.php 文件。
默认控制器
站点根 URI
当用户访问站点根目录(如 http://example.com)时,除非存在显式路由,否则将使用 $defaultController 属性指定的控制器。
默认值为 Home,对应 app/Controllers/Home.php 控制器:
public string $defaultController = 'Home';
目录 URI
在未找到匹配的路由,且 URI 恰好指向控制器目录下的某个(子)目录时,系统也会启用默认控制器。例如,当用户访问 http://example.com/admin 时,如果系统在 app/Controllers/Admin/Home.php 路径下找到了相应的控制器,那么该控制器便会被调用。
重要
无法通过控制器名称对应的 URI 来访问默认控制器。当默认控制器是 Home 时,可以访问 http://example.com/,但如果访问 http://example.com/home,则会返回 404 错误。
默认方法
此设置的工作原理与默认控制器相似,但其用途是确定默认方法。当系统找到了匹配 URI 的控制器,但 URI 中却不存在用于指定方法的分段时,系统便会转而使用这个默认方法。默认值为 index。
在此示例中,如果用户访问 example.com/products,而此时恰好存在一个 Products 控制器,那么 Products::getListAll() 方法就会被执行:
public string $defaultMethod = 'listAll';
重要
无法通过默认方法名对应的 URI 来访问该控制器。在上面的例子中,可以访问 example.com/products,但如果试图访问 example.com/products/listall,则会返回 404 错误。
将 URI 转换为大驼峰命名
Added in version 4.5.0.
备注
从 v4.6.0 开始,$translateUriToCamelCase 选项默认启用。
从 v4.5.0 开始,实现了 $translateUriToCamelCase 选项,它与当前 CodeIgniter 的编码标准配合良好。
此选项可自动将 URI 中的减号(-)转换为控制器和方法的大驼峰式命名。
例如,URI sub-dir/hello-controller/some-method 将执行 SubDir\HelloController::getSomeMethod() 方法。
备注
启用此选项时,$translateURIDashes 选项将被忽略。
禁用将 URI 转换为大驼峰命名
备注
禁用“将 URI 转换为大驼峰命名”的选项仅用于向后兼容,不建议禁用。
要禁用它,你需要在 app/Config/Routing.php 中将 $translateUriToCamelCase 选项更改为 false:
public bool $translateUriToCamelCase = false;
模块路由
Added in version 4.4.0.
即使使用 代码模块 并将控制器放置在不同命名空间,仍可使用自动路由。
要路由到模块,必须在 app/Config/Routing.php 中设置 $moduleRoutes 属性:
public array $moduleRoutes = [
'blog' => 'Acme\Blog\Controllers',
];
键名是模块的第一个 URI 分段,值是控制器命名空间。在上述配置中,http://localhost:8080/blog/foo/bar 将被路由到 Acme\Blog\Controllers\Foo::getBar()。
备注
若你定义了 $moduleRoutes,则该模块的路由将 优先 (被处理)。在上面的示例中,即便你(的应用中)存在 App\Controllers\Blog 控制器,访问 http://localhost:8080/blog 的请求也(仍)将被路由至(模块的)默认控制器 Acme\Blog\Controllers\Home。