当 Yii 应用程序开始处理请求的 URL 时,它所采取的第一步是将 URL 解析为 路由。然后使用该路由来实例化相应的 控制器操作 以处理请求。整个过程称为 *路由*。
路由的反向过程称为 *URL 创建*,它根据给定的路由和相关查询参数创建 URL。当创建的 URL 稍后被请求时,路由过程可以将它解析回原始路由和查询参数。
负责路由和 URL 创建的核心部分是 URL 管理器,它被注册为 urlManager
应用程序组件。 URL 管理器 提供 parseRequest() 方法将传入请求解析为路由和相关查询参数,以及 createUrl() 方法根据给定的路由及其相关查询参数创建 URL。
通过在应用程序配置中配置 urlManager
组件,您可以让您的应用程序识别任意 URL 格式,而无需修改您现有的应用程序代码。例如,您可以使用以下代码为 post/view
操作创建 URL
use yii\helpers\Url;
// Url::to() calls UrlManager::createUrl() to create a URL
$url = Url::to(['post/view', 'id' => 100]);
根据 urlManager
的配置,创建的 URL 可能看起来像以下之一(或其他格式)。如果稍后请求创建的 URL,它仍然会被解析回原始路由和查询参数值。
/index.php?r=post%2Fview&id=100
/index.php/post/100
/posts/100
URL 管理器 支持两种 URL 格式
默认 URL 格式使用名为 r
的 查询参数 来表示路由,并使用普通的查询参数来表示与路由关联的查询参数。例如,URL /index.php?r=post/view&id=100
表示路由 post/view
和 id
查询参数 100
。默认 URL 格式不需要任何 URL 管理器 的配置,可以在任何 Web 服务器设置中工作。
漂亮 URL 格式使用入口脚本名称后面的额外路径来表示路由和相关的查询参数。例如,URL /index.php/post/100
中的额外路径是 /post/100
,它可能表示路由 post/view
和 id
查询参数 100
,使用适当的 URL 规则。要使用漂亮 URL 格式,您需要根据实际关于 URL 应该如何显示的要求设计一组 URL 规则。
可以通过切换 enablePrettyUrl 属性在两种 URL 格式之间切换,而无需更改任何其他应用程序代码。
路由包括两个步骤
当使用默认的 URL 格式时,将请求解析为路由就像获取名为 r
的 GET
查询参数的值一样简单。
当使用漂亮的 URL 格式时,URL 管理器 将检查已注册的 URL 规则 以找到匹配项,该匹配项可以将请求解析为路由。如果找不到这样的规则,将抛出 yii\web\NotFoundHttpException 异常。
请求解析为路由后,就该创建路由标识的控制器操作了。路由将通过其中的斜杠拆分为多个部分。例如,site/index
将被拆分为 site
和 index
。每个部分都是一个 ID,它可以引用模块、控制器或操作。从路由中的第一部分开始,应用程序采取以下步骤来创建模块(如果有)、控制器和操作
在上述步骤中,如果发生任何错误,将抛出 yii\web\NotFoundHttpException,表示路由过程失败。
当请求解析为一个空路由时,将使用所谓的默认路由。默认情况下,默认路由为 site/index
,它引用 site
控制器的 index
操作。您可以通过在应用程序配置中配置应用程序的 defaultRoute 属性来对其进行自定义,如下所示
[
// ...
'defaultRoute' => 'main/index',
];
类似于应用程序的默认路由,模块也有默认路由,因此例如,如果有一个 user
模块并且请求解析为路由 user
,则模块的 defaultRoute 用于确定控制器。默认情况下,控制器名称为 default
。如果 defaultRoute 中未指定操作,则使用控制器的 defaultAction 属性来确定操作。在本例中,完整的路由将为 user/default/index
。
catchAll
路由 ¶有时,您可能希望将 Web 应用程序暂时置于维护模式,并为所有请求显示相同的提示信息页面。有很多方法可以实现此目标。但最简单的方法之一是在应用程序配置中配置 yii\web\Application::$catchAll 属性,如下所示
[
// ...
'catchAll' => ['site/offline'],
];
使用上述配置,site/offline
操作将用于处理所有传入的请求。
catchAll
属性应接受一个数组,其第一个元素指定一个路由,其余元素(名称-值对)指定要 绑定到操作 的参数。
信息:当启用此属性时,开发环境中的 调试工具栏 将无法工作。
Yii 提供了一个助手方法 yii\helpers\Url::to(),用于从给定的路由及其关联的查询参数创建各种类型的 URL。例如,
use yii\helpers\Url;
// creates a URL to a route: /index.php?r=post%2Findex
echo Url::to(['post/index']);
// creates a URL to a route with parameters: /index.php?r=post%2Fview&id=100
echo Url::to(['post/view', 'id' => 100]);
// creates an anchored URL: /index.php?r=post%2Fview&id=100#content
echo Url::to(['post/view', 'id' => 100, '#' => 'content']);
// creates an absolute URL: https://www.example.com/index.php?r=post%2Findex
echo Url::to(['post/index'], true);
// creates an absolute URL using the https scheme: https://www.example.com/index.php?r=post%2Findex
echo Url::to(['post/index'], 'https');
请注意,在上面的示例中,我们假设使用的是默认的 URL 格式。如果启用了漂亮的 URL 格式,根据正在使用的 URL 规则,创建的 URL 将有所不同。
传递给 yii\helpers\Url::to() 方法的路由是上下文相关的。它可以是相对路由,也可以是绝对路由,它将根据以下规则进行规范化
从 2.0.2 版本开始,您可以使用 别名 来指定路由。如果是这种情况,别名将首先被转换为实际的路由,然后根据上述规则转换为绝对路由。
例如,假设当前模块为 admin
,当前控制器为 post
,
use yii\helpers\Url;
// currently requested route: /index.php?r=admin%2Fpost%2Findex
echo Url::to(['']);
// a relative route with action ID only: /index.php?r=admin%2Fpost%2Findex
echo Url::to(['index']);
// a relative route: /index.php?r=admin%2Fpost%2Findex
echo Url::to(['post/index']);
// an absolute route: /index.php?r=post%2Findex
echo Url::to(['/post/index']);
// using an alias "@posts", which is defined as "/post/index": /index.php?r=post%2Findex
echo Url::to(['@posts']);
yii\helpers\Url::to() 方法通过调用 createUrl() 和 createAbsoluteUrl() 方法来实现,URL 管理器。在接下来的几个小节中,我们将解释如何配置 URL 管理器 来自定义创建的 URL 的格式。
yii\helpers\Url::to() 方法还支持创建与特定路由无关的 URL。在这种情况下,您应该传递一个字符串而不是将数组作为第一个参数传递。例如,
use yii\helpers\Url;
// currently requested URL: /index.php?r=admin%2Fpost%2Findex
echo Url::to();
// an aliased URL: https://example.com
Yii::setAlias('@example', 'https://example.com/');
echo Url::to('@example');
// an absolute URL: https://example.com/images/logo.gif
echo Url::to('/images/logo.gif', true);
除了 to()
方法之外,yii\helpers\Url 帮助程序类还提供了其他一些方便的 URL 创建方法。例如,
use yii\helpers\Url;
// home page URL: /index.php?r=site%2Findex
echo Url::home();
// the base URL, useful if the application is deployed in a sub-folder of the Web root
echo Url::base();
// the canonical URL of the currently requested URL
// see https://en.wikipedia.org/wiki/Canonical_link_element
echo Url::canonical();
// remember the currently requested URL and retrieve it back in later requests
Url::remember();
echo Url::previous();
要使用漂亮的 URL,请在应用程序配置中配置 urlManager
组件,如下所示
[
'components' => [
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'enableStrictParsing' => false,
'rules' => [
// ...
],
],
],
]
enablePrettyUrl 属性是必需的,因为它切换漂亮的 URL 格式。其余属性是可选的。但是,上面显示的配置是最常用的。
/index.php/post/100
,而是通过将此属性设置为 false
,将生成 URL /post/100
。注意:为了在创建的 URL 中隐藏入口脚本名称,除了将 showScriptName 设置为
false
之外,您可能还需要配置您的 Web 服务器,使其能够正确识别当请求的 URL 未显式指定一个时应执行哪个 PHP 脚本。如果您使用的是 Apache 或 nginx Web 服务器,您可以参考 安装 部分中描述的推荐配置。
URL 规则是一个实现 yii\web\UrlRuleInterface 的类,通常是 yii\web\UrlRule。每个 URL 规则都包含一个用于匹配 URL 路径信息部分的模式、一个路由和几个查询参数。如果 URL 规则的模式与请求的 URL 匹配,则可以使用它来解析请求。如果 URL 规则的路由和查询参数名称与给定的名称匹配,则可以使用它来创建 URL。
当启用了漂亮的 URL 格式时,URL 管理器 使用在其 rules 属性中声明的 URL 规则来解析传入的请求和创建 URL。特别是,要解析传入的请求,URL 管理器 会按声明顺序检查规则,并查找与请求的 URL 匹配的第一个规则。然后使用匹配的规则将 URL 解析为路由及其关联的参数。类似地,要创建 URL,URL 管理器 会查找与给定路由和参数匹配的第一个规则,并使用它来创建 URL。
您可以将 yii\web\UrlManager::$rules 配置为一个数组,其中键为 模式,值为相应的 路由。每个模式-路由对构成一个 URL 规则。例如,以下 rules 配置声明了两个 URL 规则。第一个规则匹配 URL posts
并将其映射到路由 post/index
。第二个规则匹配与正则表达式 post/(\d+)
匹配的 URL,并将其映射到路由 post/view
,并定义一个名为 id
的查询参数。
'rules' => [
'posts' => 'post/index',
'post/<id:\d+>' => 'post/view',
]
信息:规则中的模式用于匹配 URL 的路径信息部分。例如,
/index.php/post/100?source=ad
的路径信息为post/100
(忽略前导和尾随斜杠),它与模式post/(\d+)
匹配。
除了将 URL 规则声明为模式-路由对之外,您还可以将它们声明为配置数组。每个配置数组用于配置单个 URL 规则对象。当您想要配置 URL 规则的其他属性时,这通常是必需的。例如,
'rules' => [
// ...other url rules...
[
'pattern' => 'posts',
'route' => 'post/index',
'suffix' => '.json',
],
]
默认情况下,如果您没有为规则配置指定 class
选项,它将采用默认类 yii\web\UrlRule,这是在 yii\web\UrlManager::$ruleConfig 中定义的默认值。
URL 规则可以与命名查询参数相关联,这些参数在模式中以 <ParamName:RegExp>
的格式指定,其中 ParamName
指定参数名称,RegExp
是一个可选的正则表达式,用于匹配参数值。如果未指定 RegExp
,则表示参数值应为不带任何斜杠的字符串。
注意:您只能在参数内部使用正则表达式。模式的其余部分被视为纯文本。
当使用规则解析 URL 时,它将使用与 URL 对应部分匹配的值填充关联的参数,这些参数将在 request
应用程序组件通过 $_GET
之后可用。当使用规则创建 URL 时,它将获取提供的参数的值,并将它们插入参数声明的位置。
让我们使用一些示例来说明命名参数的工作原理。假设我们声明了以下三个 URL 规则
'rules' => [
'posts/<year:\d{4}>/<category>' => 'post/index',
'posts' => 'post/index',
'post/<id:\d+>' => 'post/view',
]
当使用规则解析 URL 时
/index.php/posts
使用第二个规则解析为路由 post/index
;/index.php/posts/2014/php
使用第一个规则解析为路由 post/index
、值为 2014 的 year
参数和值为 php
的 category
参数;/index.php/post/100
使用第三个规则解析为路由 post/view
和值为 100 的 id
参数;/index.php/posts/php
当 yii\web\NotFoundHttpException 为 true
时,将导致 yii\web\UrlManager::$enableStrictParsing,因为它与任何模式都不匹配。如果 yii\web\UrlManager::$enableStrictParsing 为 false
(默认值),则路径信息部分 posts/php
将被返回作为路由。这将要么执行相应的操作(如果存在),要么抛出 yii\web\NotFoundHttpException。当使用规则创建 URL 时
Url::to(['post/index'])
使用第二个规则创建 /index.php/posts
;Url::to(['post/index', 'year' => 2014, 'category' => 'php'])
使用第一个规则创建 /index.php/posts/2014/php
;Url::to(['post/view', 'id' => 100])
使用第三个规则创建 /index.php/post/100
;Url::to(['post/view', 'id' => 100, 'source' => 'ad'])
使用第三个规则创建 /index.php/post/100?source=ad
。因为 source
参数未在规则中指定,所以它作为查询参数附加到创建的 URL 中。Url::to(['post/index', 'category' => 'php'])
使用任何规则都创建 /index.php/post/index?category=php
。请注意,由于没有规则适用,因此 URL 是通过简单地将路由附加为路径信息以及所有参数作为查询字符串部分创建的。您可以在 URL 规则的路由中嵌入参数名称。这允许 URL 规则用于匹配多个路由。例如,以下规则在路由中嵌入 controller
和 action
参数。
'rules' => [
'<controller:(post|comment)>/create' => '<controller>/create',
'<controller:(post|comment)>/<id:\d+>/<action:(update|delete)>' => '<controller>/<action>',
'<controller:(post|comment)>/<id:\d+>' => '<controller>/view',
'<controller:(post|comment)>s' => '<controller>/index',
]
要解析 URL /index.php/comment/100/update
,将应用第二个规则,它将 controller
参数设置为 comment
,action
参数设置为 update
。因此,路由 <controller>/<action>
被解析为 comment/update
。
类似地,要为路由 comment/index
创建一个 URL,将应用最后一个规则,它将创建一个 URL /index.php/comments
。
信息:通过参数化路由,可以大大减少 URL 规则的数量,这可以显着提高 URL 管理器 的性能。
默认情况下,规则中声明的所有参数都是必需的。如果请求的 URL 不包含特定参数,或者如果在没有特定参数的情况下创建 URL,则该规则将不适用。要使某些参数可选,您可以配置规则的 defaults 属性。此属性中列出的参数是可选的,并且当它们未提供时将采用指定的值。
在以下规则声明中,page
和 tag
参数都是可选的,并且当它们未提供时将分别采用值 1 和空字符串。
'rules' => [
// ...other rules...
[
'pattern' => 'posts/<page:\d+>/<tag>',
'route' => 'post/index',
'defaults' => ['page' => 1, 'tag' => ''],
],
]
以上规则可用于解析或创建以下任何 URL
/index.php/posts
:page
为 1,tag
为 ''。/index.php/posts/2
:page
为 2,tag
为 ''。/index.php/posts/2/news
:page
为 2,tag
为 'news'
。/index.php/posts/news
:page
为 1,tag
为 'news'
。如果不使用可选参数,则需要创建 4 个规则才能实现相同的结果。
注意:如果 pattern 仅包含可选参数和斜杠,则只有当所有其他参数被省略时才能省略第一个参数。
可以在 URL 规则的模式中包含 Web 服务器名称。这主要在您的应用程序应针对不同的 Web 服务器名称表现不同时有用。例如,以下规则将 URL https://admin.example.com/login
解析为路由 admin/user/login
,将 https://www.example.com/login
解析为 site/login
。
'rules' => [
'https://admin.example.com/login' => 'admin/user/login',
'https://www.example.com/login' => 'site/login',
]
您还可以在服务器名称中嵌入参数以从中提取动态信息。例如,以下规则将 URL https://en.example.com/posts
解析为路由 post/index
和参数 language=en
。
'rules' => [
'https://<language:\w+>.example.com/posts' => 'post/index',
]
从 2.0.11 版本开始,您还可以使用适用于 http
和 https
的协议相对模式。语法与上面相同,但跳过 http:
部分,例如:'//www.example.com/login' => 'site/login'
。
注意:包含服务器名称的规则 **不应** 在其模式中包含入口脚本的子文件夹。例如,如果应用程序的入口脚本位于
https://www.example.com/sandbox/blog/index.php
,那么您应该使用模式https://www.example.com/posts
而不是https://www.example.com/sandbox/blog/posts
。这将允许您的应用程序在任何目录下部署,而无需更改您的 url 规则。Yii 将自动检测应用程序的基准 URL。
您可能希望出于各种目的将后缀添加到 URL。例如,您可能将 .html
添加到 URL,使它们看起来像静态 HTML 页面的 URL;您也可以将 .json
添加到 URL 以指示响应的预期内容类型。您可以通过在应用程序配置中配置 yii\web\UrlManager::$suffix 属性来实现此目标
[
// ...
'components' => [
'urlManager' => [
'enablePrettyUrl' => true,
// ...
'suffix' => '.html',
'rules' => [
// ...
],
],
],
]
以上配置将允许 URL 管理器 识别请求的 URL,并创建以 .html
为后缀的 URL。
提示:您可以将
/
设置为 URL 后缀,以便所有 URL 都以斜杠结尾。
注意:当您配置 URL 后缀时,如果请求的 URL 没有后缀,它将被视为未识别的 URL。这是 SEO(搜索引擎优化)的推荐做法,以避免在不同 URL 上出现重复内容。
有时您可能希望对不同的 URL 使用不同的后缀。这可以通过配置单个 URL 规则的 suffix 属性来实现。当 URL 规则设置了此属性时,它将覆盖 URL 管理器 级别上的后缀设置。例如,以下配置包含一个自定义 URL 规则,它使用 .json
作为其后缀,而不是全局的 .html
后缀。
[
'components' => [
'urlManager' => [
'enablePrettyUrl' => true,
// ...
'suffix' => '.html',
'rules' => [
// ...
[
'pattern' => 'posts',
'route' => 'post/index',
'suffix' => '.json',
],
],
],
],
]
在实现 RESTful API 时,通常需要根据使用的 HTTP 方法将同一个 URL 解析为不同的路由。这可以通过将支持的 HTTP 方法作为前缀添加到规则的模式中来轻松实现。如果规则支持多个 HTTP 方法,则用逗号分隔方法名称。例如,以下规则具有相同的模式 post/<id:\d+>
,但具有不同的 HTTP 方法支持。对 PUT post/100
的请求将被解析为 post/update
,而对 GET post/100
的请求将被解析为 post/view
。
'rules' => [
'PUT,POST post/<id:\d+>' => 'post/update',
'DELETE post/<id:\d+>' => 'post/delete',
'post/<id:\d+>' => 'post/view',
]
注意:如果 URL 规则在其模式中包含 HTTP 方法,则该规则仅用于解析目的,除非
GET
位于指定的动词中。当 URL 管理器 被调用以创建 URL 时,它将被跳过。
提示:为了简化 RESTful API 的路由,Yii 提供了一个特殊的 URL 规则类 yii\rest\UrlRule,它非常有效,并支持一些花哨的功能,例如控制器 ID 的自动复数化。有关更多详细信息,请参阅 RESTful API 章节中的 路由 部分。
URL 规则可以动态添加到 URL 管理器 中。可再分配的 模块 通常需要这样做,它们希望管理自己的 URL 规则。为了使动态添加的规则在路由过程中生效,您应该在应用程序的 引导 阶段添加它们。对于模块,这意味着它们应该实现 yii\base\BootstrapInterface 并在 bootstrap() 方法中添加规则,如下所示
public function bootstrap($app)
{
$app->getUrlManager()->addRules([
// rule declarations here
], false);
}
请注意,您还应该在 yii\web\Application::bootstrap() 中列出这些模块,以便它们可以参与 引导 过程。
尽管默认的 yii\web\UrlRule 类对于大多数项目来说足够灵活,但在某些情况下您必须创建自己的规则类。例如,在汽车经销商网站上,您可能希望支持 URL 格式,如 /Manufacturer/Model
,其中 Manufacturer
和 Model
都必须与数据库表中存储的一些数据匹配。默认的规则类在这里将不起作用,因为它依赖于静态声明的模式。
我们可以创建以下 URL 规则类来解决这个问题。
<?php
namespace app\components;
use yii\web\UrlRuleInterface;
use yii\base\BaseObject;
class CarUrlRule extends BaseObject implements UrlRuleInterface
{
public function createUrl($manager, $route, $params)
{
if ($route === 'car/index') {
if (isset($params['manufacturer'], $params['model'])) {
return $params['manufacturer'] . '/' . $params['model'];
} elseif (isset($params['manufacturer'])) {
return $params['manufacturer'];
}
}
return false; // this rule does not apply
}
public function parseRequest($manager, $request)
{
$pathInfo = $request->getPathInfo();
if (preg_match('%^(\w+)(/(\w+))?$%', $pathInfo, $matches)) {
// check $matches[1] and $matches[3] to see
// if they match a manufacturer and a model in the database.
// If so, set $params['manufacturer'] and/or $params['model']
// and return ['car/index', $params]
}
return false; // this rule does not apply
}
}
并在 yii\web\UrlManager::$rules 配置中使用新规则类
'rules' => [
// ...other rules...
[
'class' => 'app\components\CarUrlRule',
// ...configure other properties...
],
]
从 2.0.10 版本开始,UrlManager 可以配置为使用 UrlNormalizer 来处理同一个 URL 的变体,例如带或不带尾部斜杠的变体。从技术上讲,https://example.com/path
和 https://example.com/path/
是不同的 URL,为两者提供相同的内容会降低 SEO 排名。默认情况下,规范器会折叠连续的斜杠,根据后缀是否有尾部斜杠添加或删除尾部斜杠,并使用 永久重定向 重定向到 URL 的规范化版本。规范器可以在全局范围内为 URL 管理器配置,也可以在每个规则中单独配置 - 默认情况下,每个规则都将使用来自 URL 管理器的规范器。您可以将 UrlRule::$normalizer 设置为 false
以禁用特定 URL 规则的规范化。
以下显示了 UrlNormalizer 的示例配置
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'enableStrictParsing' => true,
'suffix' => '.html',
'normalizer' => [
'class' => 'yii\web\UrlNormalizer',
// use temporary redirection instead of permanent for debugging
'action' => UrlNormalizer::ACTION_REDIRECT_TEMPORARY,
],
'rules' => [
// ...other rules...
[
'pattern' => 'posts',
'route' => 'post/index',
'suffix' => '/',
'normalizer' => false, // disable normalizer for this rule
],
[
'pattern' => 'tags',
'route' => 'tag/index',
'normalizer' => [
// do not collapse consecutive slashes for this rule
'collapseSlashes' => false,
],
],
],
]
注意:默认情况下,UrlManager::$normalizer 已禁用。您需要显式配置它才能启用 URL 规范化。
在开发复杂的 Web 应用程序时,优化 URL 规则非常重要,这样可以减少解析请求和创建 URL 所花费的时间。
通过使用参数化路由,您可以减少 URL 规则的数量,从而显著提高性能。
在解析或创建 URL 时,URL 管理器 会按照声明顺序检查 URL 规则。因此,您可以考虑调整 URL 规则的顺序,以便将更具体的和/或更常用的规则放在使用较少的规则之前。
如果一些 URL 规则在其模式或路由中共享相同的前缀,您可以考虑使用 yii\web\GroupUrlRule,以便它们可以作为一组被 URL 管理器 更有效地检查。当您的应用程序由模块组成时,这种情况经常发生,每个模块都有自己的一组 URL 规则,它们的模块 ID 作为共同前缀。
发现错别字或您认为此页面需要改进?
在 github 上编辑它 !
请注册或登录以发表评论。