您的位置:首页 > 其它

2017_01_03_02_路由

2017-01-04 22:21 363 查看
路由:http://hostname/index.php?r=site/say&message=Hello+World

当入口脚本在调用 yii\web\Application::run() 方法时,它进行的第一个操作就是解析输入的请求,然后实例化对应的控制器操作处理这个请求。 该过程就被称为引导路由(routing)。

 message 被作为一个参数传给 actionSay() 方法,当省略它时,参数将使用默认值代替。

上面 URL 中的参数 r 代表路由,是整个应用级的, 指向特定操作的独立 ID。

路由格式是 控制器 ID/操作 ID。应用接受请求的时候会检查参数, 使用控制器 ID 去确定哪个控制器应该被用来处理请求。 然后相应控制器将使用操作 ID 去确定哪个操作方法将被用来做具体工作。 上述例子中,路由 site/say 将被解析至 SiteController 控制器和其中的 say 操作。 因此 SiteController::actionSay() 方法将被调用处理请求。

注意:与操作一样,一个应用中控制器同样有唯一的 ID。 控制器 ID 和操作 ID 使用同样的命名规则。 控制器的类名源自于控制器 ID,移除了连字符 ,每个单词首字母大写,并加上 Controller 后缀。 例子:控制器 ID post-comment 相当于控制器类名 PostCommentController。

终端用户通过所谓的路由寻找到操作,路由是包含以下部分的字符串:

模型ID: 仅存在于控制器属于非应用的模块;

控制器ID: 同应用(或同模块如果为模块下的控制器) 下唯一标识控制器的字符串;

操作ID: 同控制器下唯一标识操作的字符串。

路由使用如下格式:

ControllerID/ActionID

如果属于模块下的控制器,使用如下格式:

ModuleID/ControllerID/ActionID

如果用户的请求地址为 http://hostname/index.php?r=site/index, 会执行site 控制器的index 操作。

Yii为开发者提供了路由和URL管理组件:

所谓路由是指URL中用于标识用于处理用户请求的module, controller, action的部分, 一般情况下由 r 查询参数来指定。 如 http://www.digpage.com/index.php?r=post/view&id=100 , 表示这个请求将由PostController 的 actionView来处理。

同时,Yii也提供了一种美化URL的功能,使得上面的URL可以用一个比较整洁、美观的形式表现出来, 如 http://www.digpage.com/post/view/100 。 这个功能的实现是依赖于一个称为 urlManager 的应用组件。

使用 urlManager 开发者可以解析用户的请求,并指派相应的module, controller和action来进行处理, 还可以根据预义的路由规则,生成需要的URL返回给用户使用。 简而言之,urlManger具有解析请求以便确定指派谁来处理请求和根据路由规则生成URL 2个功能。

===================例子===================================

当前只是理解如何使用路由,具体怎么解析和生成路由,是由路由规则决定的

美化URL

一般情况下,Yii应用生成和接受形如 http://www.digpage.com/index.php?r=post/view&id=100 的URL。这个URL分成几个部分:

    表示主机信息的 http://www.digapge.com
    表示入口脚本的 index.php

    表示路由的 r=post/view

    表示普通查询参数的 id=100

其中,主机信息部分从URL来讲,一般是不能少的。当然内部链接可以使用相对路径,这种情况下看似 可以省略,但是User Agent最终发出Request时,也是包含主机信息的。换句话说,Web Server接收并 转交给Yii处理的URL,是完整的、带有主机信息的URL。

而入口脚本 index.php 我们知道,Web Server会将所有的请求都是交由其进行处理。 也就是说,Web Server应当视所有的URL为请求 index.php 脚本。这在 :ref:install 部分我们 已经对Web Server进行过相应配置了。

Yii允许我们不在URL中出现入口脚本 index.php 。

其次,路由信息对于Yii应用而言也必不可少,表明应当使用哪个controller和action来处理请求, 否则Yii只能使用默认的路由来处理请求。这个形式比较固定,采用的是一种类似路径的形式, 一般为 module/controller/action 之类的。

如果将URL省略掉入口脚本,并将路由信息转换成路径,上面的URL就会变成: http://www.digpage.com/post/view?id=100
对于查询参数 id=100 而言,这个URL请求的是编号为100的一个POST, 并执行view操作。那么我们可以再进一步改成 http://www.digpage.com/post/view/100
我们假如所请求的编号100的文章,其标题为 Route , 那么不妨使用用 http://www.digpage.com/post/view/Route 来访问。

这样的话,干脆再加上 .html 好了。 变成 http://www.digpage.com/post/view/Route.html    

Yii有专门的 yii\web\UrlManager 来进行处理,其中:

隐藏入口脚本可以通过 yii\web\UrlManager::showScriptName = false 来实现

路由的路径化可以通过 yii\web\UrlManager::enablePrettyUrl = true 来实现

参数的路径化可以通过路由规则来实现

假后缀(fake suffix) .html 可以通过 yii\web\UrlManager::suffix = '.html' 来实现

路由规则:

路由规则是指 urlManager 用于解析请求或生成URL的规则。 一个路由规则必须实现 yii\web\UrlRuleInterface 接口,这个接口定义了两个方法:

    用于解析请求的 yii\web\UrlRuleInterface::parseRequest()

    用于生成URL的 yii\web\UrlRuleInterface::createUrl()

Yii中,使用 yii\web\UrlRule 来表示路由规则,一般这个类是足够开发者使用的。 但是,如果开发者想自己实现解析请求或生成URL的逻辑,可以以这个类为基类进行派生, 并重载 parseRuquest() 和 createUrl() 。

以下是配置文件中urlManager组件的路由规则配置部分,以几个相对简单、典型的路由规则的为例, 先有个感性认识:

'rules' => [

    // 为路由指定了一个别名,以 post 的复数形式来表示 post/index 路由

    'posts' => 'post/index',

    // id 是命名参数,post/100 形式的URL,其实是 post/view&id=100

    'post/<id:\d+>' => 'post/view',

    // controller action 和 id 以命名参数形式出现

    '<controller:(post|comment)>/<id:\d+>/<action:(create|update|delete)>'

        => '<controller>/<action>',

    // 包含了 HTTP 方法限定,仅限于DELETE方法

    'DELETE <controller:\w+>/<id:\d+>' => '<controller>/delete',

    // 需要将 Web Server 配置成可以接收 *.digpage.com 域名的请求

    'http://<user:\w+>.digpage.com/<lang:\w+>/profile' => 'user/profile',

]

只需大致了解上面这个数组用于为urlManager声明路由规则。

数组的键相当于请求(需要解析的或将要生成的),而元素的值则对应的路由, 即 controller/action 。

请求部分可称为pattern,路由部分则可称为route。

yii\web\UrlRule 的代码:

class UrlRule extends Object implements UrlRuleInterface

{

    // 用于 $mode 表示路由规则的2种工作模式:仅用于解析请求和仅用于生成URL。

    // 任意不为1或2的值均表示两种模式同时适用,

    // 一般未设定或为0时即表示两种模式均适用。

    const PARSING_ONLY = 1;

    const CREATION_ONLY = 2;

    // 路由规则名称

    public $name;

    // 用于解析请求或生成URL的模式,通常是正则表达式

    public $pattern;

    // 用于解析或创建URL时,处理主机信息的部分,如 http://www.digpage.com
    public $host;

    // 指向controller 和 action 的路由

    public $route;

    // 以一组键值对数组指定若干GET参数,在当前规则用于解析请求时,

    // 这些GET参数会被注入到 $_GET 中去

    public $defaults = [];

    // 指定URL的后缀,通常是诸如 ".html" 等,

    // 使得一个URL看起来好像指向一个静态页面。

    // 如果这个值未设定,使用 UrlManager::suffix 的值。

    public $suffix;

    // 指定当前规则适用的HTTP方法,如 GET, POST, DELETE 等。

    // 可以使用数组表示同时适用于多个方法。

    // 如果未设定,表明当前规则适用于所有方法。

    // 当然,这个属性仅在解析请求时有效,在生成URL时是无效的。

    public $verb;

    // 表明当前规则的工作模式,取值可以是 0, PARSING_ONLY, CREATION_ONLY。

    // 未设定时等同于0。

    public $mode;

    // 表明URL中的参数是否需要进行url编码,默认是进行。

    public $encodeParams = true;

    // 用于生成新URL的模板

    private $_template;

    // 一个用于匹配路由部分的正则表达式,用于生成URL

    private $_routeRule;

    // 用于保存一组匹配参数的正则表达式,用于生成URL

    private $_paramRules = [];

    // 保存一组路由中使用的参数

    private $_routeParams = [];

    // 初始化

    public function init() {...}

    // 用于解析请求,由UrlRequestInterface接口要求

    public function parseRequest($manager, $request) {...}

    // 用于生成URL,由UrlRequestInterface接口要求

    public function createUrl($manager, $route, $params) {...}

}

从上面代码看, UrlRule 的属性(可配置项)比较多。

要着重分析一下初始化函数 yii\web\UrlRule::init() ,来加深对这些属性的理解:

public function init()

{

    // 一个路由规则必定要有 pattern ,否则是没有意义的,

    // 一个什么都没规定的规定,要来何用?

    if ($this->pattern === null) {

        throw new InvalidConfigException('UrlRule::pattern must be set.');

    }

    // 不指定规则匹配后所要指派的路由,Yii怎么知道将请求交给谁来处理?

    // 不指定路由,Yii怎么知道这个规则可以为谁创建URL?

    if ($this->route === null) {

        throw new InvalidConfigException('UrlRule::route must be set.');

    }

    // 如果定义了一个或多个verb,说明规则仅适用于特定的HTTP方法。

    // 既然是HTTP方法,那就要全部大写。

    // verb的定义可以是字符串(单一的verb)或数组(单一或多个verb)。

    if ($this->verb !== null) {

        if (is_array($this->verb)) {

            foreach ($this->verb as $i => $verb) {

                $this->verb[$i] = strtoupper($verb);

            }

        } else {

            $this->verb = [strtoupper($this->verb)];

        }

    }

    // 若未指定规则的名称,那么使用最能区别于其他规则的 $pattern

    // 作为规则的名称

    if ($this->name === null) {

        $this->name = $this->pattern;

    }

    // 删除 pattern 两端的 "/",特别是重复的 "/",

    // 在写 pattern 时,虽然有正则的成分,但不需要在两端加上 "/",

    // 更不能加上 "#" 等其他分隔符

    $this->pattern = trim($this->pattern, '/');

    // 如果定义了 host ,将 host 部分加在 pattern 前面,作为新的 pattern

    if ($this->host !== null) {

        // 写入的host末尾如果已经包含有 "/" 则去掉,特别是重复的 "/"

        $this->host = rtrim($this->host, '/');

        $this->pattern = rtrim($this->host . '/' . $this->pattern, '/');

    // 既未定义 host ,pattern 又是空的,那么 pattern 匹配任意字符串。

    // 而基于这个pattern的,用于生成的URL的template就是空的,

    // 意味着使用该规则生成所有URL都是空的。

    // 后续也无需再作其他初始化工作了。

    } elseif ($this->pattern === '') {

        $this->_template = '';

        $this->pattern = '#^$#u';

        return;

    // pattern 不是空串,且包含有 '://',以此认定该pattern包含主机信息

    } elseif (($pos = strpos($this->pattern, '://')) !== false) {

        // 除 '://' 外,第一个 '/' 之前的内容就是主机信息

        if (($pos2 = strpos($this->pattern, '/', $pos + 3)) !== false) {

            $this->host = substr($this->pattern, 0, $pos2);

        // '://' 后再无其他 '/',那么整个 pattern 其实就是主机信息

        } else {

            $this->host = $this->pattern;

        }

    // pattern 不是空串,且不包含主机信息,两端加上 '/' ,形成一个正则

    } else {

        $this->pattern = '/' . $this->pattern . '/';

    }

    // route 也要去掉两头的 '/'

    $this->route = trim($this->route, '/');

    // 从这里往下,请结合流程图来看

    // route 中含有 <参数> ,则将所有参数提取成 [参数 => <参数>]

    // 存入 _routeParams[],

    // 如 ['controller' => '<controller>', 'action' => '<action>'],

    // 留意这里的短路判断,先使用 strpos(),快速排除无需使用正则的情况

    if (strpos($this->route, '<') !== false &&

        preg_match_all('/<(\w+)>/', $this->route, $matches)) {

        foreach ($matches[1] as $name) {

            $this->_routeParams[$name] = "<$name>";

        }

    }

    // 这个 $tr[] 和 $tr2[] 用于字符串的转换

    $tr = [

        '.' => '\\.',

        '*' => '\\*',

        '$' => '\\$',

        '[' => '\\[',

        ']' => '\\]',

        '(' => '\\(',

        ')' => '\\)',

    ];

    $tr2 = [];

    // pattern 中含有 <参数名:参数pattern> ,

    // 其中 ':参数pattern' 部分是可选的。

    if (preg_match_all('/<(\w+):?([^>]+)?>/', $this->pattern, $matches,

        PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {

        foreach ($matches as $match) {

            // 获取 “参数名”

            $name = $match[1][0];

            // 获取 “参数pattern” ,如果未指定,使用 '[^\/]' ,

            // 表示匹配除 '/' 外的所有字符

            $pattern = isset($match[2][0]) ? $match[2][0] : '[^\/]+';

            // 如果 defaults[] 中有同名参数,

            if (array_key_exists($name, $this->defaults)) {

                // $match[0][0] 是整个 <参数名:参数pattern> 串

                $length = strlen($match[0][0]);

                $offset = $match[0][1];

                // pattern 中 <参数名:参数pattern> 两头都有 '/'

                if ($offset > 1 && $this->pattern[$offset - 1] === '/'

                    && $this->pattern[$offset + $length] === '/') {

                    // 留意这个 (?P<name>pattern) 正则,这是一个命名分组。

                    // 仅冠以一个命名供后续引用,使用上与直接的 (pattern) 没有区别

                    // 见:http://php.net/manual/en/regexp.reference.subpatterns.php

                    $tr["/<$name>"] = "(/(?P<$name>$pattern))?";

                } else {

                    $tr["<$name>"] = "(?P<$name>$pattern)?";

                }

            // defaults[]中没有同名参数

            } else {

                $tr["<$name>"] = "(?P<$name>$pattern)";

            }

            // routeParams[]中有同名参数

            if (isset($this->_routeParams[$name])) {

                $tr2["<$name>"] = "(?P<$name>$pattern)";

            // routeParams[]中没有同名参数,则将 参数pattern 存入 _paramRules[] 中。

            // 留意这里是怎么对  参数pattern  进行处理后再保存的。

            } else {

                $this->_paramRules[$name] = $pattern === '[^\/]+' ? '' :

                    "#^$pattern$#u";

            }

        }

    }

    // 将 pattern 中所有的 <参数名:参数pattern> 替换成 <参数名> 后作为 _template

    $this->_template = preg_replace('/<(\w+):?([^>]+)?>/', '<$1>', $this->pattern);

    // 将 _template 中的特殊字符及字符串使用 tr[] 进行转换,并作为最终的pattern

    $this->pattern = '#^' . trim(strtr($this->_template, $tr), '/') . '$#u';

    // 如果指定了 routePrams 还要使用 tr2[] 对 route 进行转换,

    // 并作为最终的 _routeRule

    if (!empty($this->_routeParams)) {

        $this->_routeRule = '#^' . strtr($this->route, $tr2) . '$#u';

    }

}

    先看init() 的前半部分,这些代码提醒我们:

    规则的 $pattern 和 $route 是必须配置的。

    规则的名称 $name 和主机信息 $host 在未配置的情况下,可以从 $pattern 来获取。

    $pattern 虽然含有正则的成分,但不需要在两端加入 / ,更不能使用 # 等其他分隔符。 Yii会自动为我们加上。

    指定 $pattern 为空串,可以使该规则匹配任意的URL。此时基于该规则所生成的所有URL也都是空串。

    $pattern 中含有 :\\ 时,Yii会认为其中包含了主机信息。此时就不应当再指定 host 。 否则,Yii会将 host 接在这个 pattern 前,作为新的pattern。这会造成该pattern 两段 :\\ , 而这显然不是我们要的。

    

    >>>>待续部分看截图 路由.png<<<<<

    

    

    

   
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: