当前位置: 首页 > web开发 > PHP > 正文

Yii 2.0处理请求

时间:2017-04-18 博客园 枫爷

缘起

这一章是Yii系列的第三章,前两章给大伙讲解了Yii2.0的安装与Yii2.0的基本框架及基础概念,传送门:

【Yii2.0的安装与调试】:http://www.cnblogs.com/riverdubu/p/6439680.html

【Yii2.0基础框架】:http://www.cnblogs.com/riverdubu/p/6607373.html

相信学习过上两章的内容,你们是不是对Yii有个大致的了解了呢,Yii2.0基础框架这一章很重要,不仅仅是因为它的长度,而是里面讲解了整个Yii2.0的基础概念,以及如何基于MVC模型构建的它的整套框架,只是浮于表面的一些基础只是,如果大家对更深层次的Yii的底层感兴趣,可以持续关注我的博客,后面我会对于Yii底层的一些关键组件和代码做更加详细的讲解。

接上一章的话,我们这章带大伙了解下一个用户的请求从发起到收到响应的整个过程是如何实现,这对于统筹全局至关重要,如果说上一章是基础,是打根基和搭架子的一章,那么这一章就是给你将整个房子建起来啦,会把所有的门窗房间规划好,让你能够轻松自如的在房子里面穿梭。

运行机制

首先,我们来看下整个请求的运行机制

用户发送请求给入口脚本,入口脚本加载配置,运行application,application会创建一个request组件去处理这次用户请求,Request组件会去路由里面查找用户想要请求的那个Controller,找到Controller后实例化它,调用对应的action执行操作,action会调用对应model层的函数进行数据处理,处理完成之后返回给对应的action,action会将数据格式化或者不格式化渲染View,为其提供填充所需要的数据,渲染完成的结果会返回给response组件发送给用户浏览器。

这里面有几个关键的组件,request和response我们先不用去管他们,这是application会自动搞定的,我们要关心关心这个被称为路由的东西。

路由

有几个关键概念是需要大家理解下的,引导路由。

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

我们之前可能看过类似于下面的请求URL

http://服务器IP/index.php?r=post/view&id=100

这边的r后面的就是后面会被实例化的Controller去处理这次请求,这是标准的写法,但是,为了让URL看上去更pretty一些,比方说下面的URL

http://服务器IP/post/view?id=100

这样是不是能够更清晰的看出是哪个业务单元的逻辑,这样的写法需要通过配置应用主体Components中一个叫urlManager的配置项来完成路由的解析。

这两种写法之间通过配置urlManager里面的enablePrettyUrl属性来实现切换,true的话是下面一种写法,false的话是上面一种写法。

引导路由包含两步,第一步,请求会被解析成对应的路由和请求参数,第二步,一个路由对应的Controller的action会被实例化去处理这个请求。

当你使用pretty方式去解析你的URL时,urlManager会在当前的规则中寻找你想要找的规则,如果找不到,会抛出一个 yii\web\NotFoundHttpException,这就是大名鼎鼎的404啦。

一旦找到对应的路由规则,URL会被拆分成很多部分,就像上面的post/view一样,从前往后可能分别对应了一个module,一个controller,一个action。application回一个个去尝试的,如果没有找到对应的action去执行这个请求,还是会像上面一样,抛出大名鼎鼎的404。

我们这边对基础的路由不做解析,想要了解的朋友可以去官网详细的查看下。我们来具体的来看下pretty的路由规则。

为了能够使用pretty路由解析,我们得在应用主体,也就是上一章的web.php中配置一下。

[
    'components' => [
        'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'enableStrictParsing' => false,
            'rules' => [
                // ...
            ],
        ],
    ],
]

具体的规则就要配置在rules,这里建议一下,这个rules配置在一个文件中,然后这边require一下即可。

这里面有几个参数

enablePrettyUrl:和上面的意思一样,打开pretty url规则的,这个必须得打开,要不然的话默认的路由解析会是基础路由解析。

showScriptName:是否将入口脚本写入用户请求的链接,如果为true,请求链接为:/index.php/post/100,如果为false,请求链接为:/post/100

enableStrictParsing:是否需要按照rules里的规则严格解析,如果为true,请求的链接必须符合rules里面的规则,如果不满足,抛出404,如果为false,请求链接可以是rules规则的一部分。

url rules

下面我们就来详细的讲一讲需要配置的这个rules文件。

我们先来看个例子

[
    'posts' => 'post/index', 
    'post/<id:\d+>' => 'post/view',
]

这边定义了两条URL rules

第一条将URL中的posts对应到post/index这条路由;

第二条规则匹配一个正则式post/(\d+)对应到post/view这条路由,id是它的参数。

当URL被规则解析,他后面的参数会被收录进application的request组件,后面我们会说到这个request组件。在我看来,处女座的人比较满意的一个url是这样的:http://example.com/psot/action?a=1&b=2,这是比较常规的

我这边给出的一个例子适用于大多数情况

'v1/<module:\w+>/<controller:\w+>/<action:\w+>' => 'api/v1/<module>/<controller>/<action>', //模块相关规则

前面部分是用户访问时的链接格式,后面部分是项目代码中对应的action位置。

通过正则式去解析路由,这回大大减少规则数量,大大提升urlManager的性能。

Request 

讲完了路由,我们再来看下一个请求是如何被应用主体接受和反馈的吧。

首先,我们来看下在应用中,用户的请求是被实例化成啥的。

一个应用的请求是用 yii\web\Request 对象来表示的,该对象提供了诸如 请求参数(译者注:通常是GET参数或者POST参数)、HTTP头、cookies等信息。

调用方式

Yii::$app->request

$request->get()这段代码等价于$_GET,之前和大伙说过,不能直接使用$_GET这个值,这使你更容易编写测试用例,因为你可以伪造数据来创建一个模拟请求组件。

你可以通过 Yii::$app->request->method 表达式来获取当前请求使用的HTTP方法。

$request = Yii::$app->request;

if ($request->isAjax) { /* 该请求是一个 AJAX 请求 */ }
if ($request->isGet)  { /* 请求方法是 GET */ }
if ($request->isPost) { /* 请求方法是 POST */ }
if ($request->isPut)  { /* 请求方法是 PUT */ }

我这边一般的是使用一个Web基类去获取请求的body和head

$requestBody = $this->getParam('body');
$requestHeader = $this->getParam('header');

Web基类

<?php
namespace ext\controller;

use Yii;
use yii\web\Controller;

class Web extends Controller
{
    public function init()
    {
        parent::init();
        $this->setDefaultCrumbs();
    }

    public function beforeaction($action)
    {
        return parent::beforeaction($action);
    }

    ......

    /**
     * 获取参数
     * @param null $key
     * @param null $val
     * @return array|mixed|null
     */
    public function getParam($key = null, $val = null)
    {

        $request = Yii::$app->request;

        $data = [];
        /**
         * 如果是GET请求
         */
        if ($request->getIsGet()) {
            $data = $request->get('request');
        } elseif ($request->getIsPost()) {
            if($request->contentType == 'application/json')
            {
                $rawBody = $request->rawBody;
                if($rawBody){
                    $requestData = json_decode($rawBody,true);
                    $data = $requestData['request'];
                    unset($requestData);
                }
            }else{
                $data = $request->post('request');
            }
        } elseif ($request->getIsDelete()) {
            $data = $request->get('request');
        }

        if (isset($data[$key])) {
            if (is_null($data[$key])) {
                return $val;
            } else {
                return $data[$key];
            }
        } elseif (is_null($key)) {
            return $data;
        } else {
            return $val;
        }
    }
}

详细的代码待我完成Yii系列最佳时间贡献到githun供大伙下载研究呢。