【Yii系列】处理请求
缘起
这一章是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供大伙下载研究呢。
Response
看完了请求的引导,我们来看下我们如何反馈给用户吧。
Yii是通过response这个组件来反馈给用户我们想要给到他的信息的。
Response对象包含的信息有HTTP状态码,HTTP头和主体内容等, 网页应用开发的最终目的本质上就是根据不同的请求构建这些响应对象。
响应中首当其冲的应该就是状态码了,也就是我们之前提到的404啊,503啊,301啊,这些都是服务器的状态码。
Yii提供了一些常量去定义这些状态码
yii\web\BadRequestHttpException:状态码 400。
yii\web\ConflictHttpException:状态码 409。
yii\web\ForbiddenHttpException:状态码 403。
yii\web\GoneHttpException:状态码 410。
yii\web\MethodNotAllowedHttpException:状态码 405。
yii\web\NotAcceptableHttpException:状态码 406。
yii\web\NotFoundHttpException:状态码 404。
yii\web\ServerErrorHttpException:状态码 500。
yii\web\TooManyRequestsHttpException:状态码 429。
yii\web\UnauthorizedHttpException:状态码 401。
yii\web\UnsupportedMediaTypeHttpException:状态码 415。
yii\web\Response::statusCode 状态码默认为200, 如果需要指定请求失败,可抛出对应的上诉的HTTP异常。
如果想抛出的异常不在如上列表中,可创建一个yii\web\HttpException异常, 带上状态码抛出,如下:
throw new \yii\web\HttpException(402);
可在 response 组件中操控yii\web\Response::headers来发送HTTP头部信息, 例如:
$headers = Yii::$app->response->headers; // 增加一个 Pragma 头,已存在的Pragma 头不会被覆盖。
$headers->add('Pragma', 'no-cache'); // 设置一个Pragma 头. 任何已存在的Pragma 头都会被丢弃
$headers->set('Pragma', 'no-cache'); // 删除Pragma 头并返回删除的Pragma 头的值到数组
$values = $headers->remove('Pragma');
响应主体
大多是响应应有一个主体存放你想要显示给终端用户的内容。
如果已有格式化好的主体字符串,可赋值到响应的yii\web\Response::$content属性, 例如:
$response = Yii::$app->response;
$response->format = \yii\web\Response::FORMAT_JSON;
$response->data = ['message' => 'hello world'];
Yii可以使用如下的几种格式
HTML: 通过 yii\web\HtmlResponseFormatter 来实现.
XML: 通过 yii\web\XmlResponseFormatter来实现.
JSON: 通过 yii\web\JsonResponseFormatter来实现.
JSONP: 通过 yii\web\JsonResponseFormatter来实现.
RAW: use this format if you want to send the response directly without applying any formatting.
一般的话,我们不会这么麻烦的去操作,而是直接和前端商量好,我们会以哪种形式返回给你,然后直接在action里面直接返回数据给用户。
Web基类中对应的方法:
public function sendSuccess($message = null, $data = [], $code = "0")
{
return $this->formatJson([
'response' => [
'header' => [
'code' => $code,
'msg' => $message
],
'body' => $data ?: []
]
]
);
} public function sendError($message = null, $code = "1")
{
return $this->formatJson([
'response' => [
'header' => [
'code' => (string)$code,
'msg' => $message ? $message : '未知错误'
],
]
]
);
}
session
除了Request和Response这两个基本概念,还有个概念比较重要,如果你想让你用户的数据在你后端构成一个map,你可以使用session这个组件,相信我,这个组件绝对能够满足大部分用户登录系统,用他来保存用户的数据非常方便,每次用户来请求,只需要将sessionId传给你,你就可以获取到这个用户的状态,虽然方便,但不能完全保证安全,如果sessionId被截取了,这也是相当蛋疼的事。这就要看系统的健壮性了,用户的敏感信息是否会在前端有权限拿到,如果要拿敏感信息,你的系统又当如何。
这里不多说,会在安全那一章和大伙好好唠唠后台应用中提高安全系数的一些做法呢。
下面直接上session相关的代码,session的操作也很简单,key-value模型。
$session = Yii::$app->session; // 检查session是否开启
if ($session->isActive) ... // 开启session
$session->open(); // 关闭session
$session->close(); // 销毁session中所有已注册的数据
$session->destroy();
比较重要的一个方法是设置session存在的时间:
$session->setTimeout(...)
session数据的存储和提取
$session = Yii::$app->session; // 获取session中的变量值,以下用法是相同的:
$language = $session->get('language');
$language = $session['language'];
$language = isset($_SESSION['language']) ? $_SESSION['language'] : null; // 设置一个session变量,以下用法是相同的:
$session->set('language', 'en-US');
$session['language'] = 'en-US';
$_SESSION['language'] = 'en-US'; // 删除一个session变量,以下用法是相同的:
$session->remove('language');
unset($session['language']);
unset($_SESSION['language']); // 检查session变量是否已存在,以下用法是相同的:
if ($session->has('language')) ...
if (isset($session['language'])) ...
if (isset($_SESSION['language'])) ... // 遍历所有session变量,以下用法是相同的:
foreach ($session as $name => $value) ...
foreach ($_SESSION as $name => $value) ...
Flash数据是一种特别的session数据,它一旦在某个请求中设置后, 只会在下次请求中有效,然后该数据就会自动被删除。喜欢的朋友可以去官方文当中详查。
关于cookie,后端使用的较少,前端用的时候经常用它来存储一些数据。这里不多说,感兴趣的朋友可以去官方文当中详查。
至此,所有的关于用户的请求Yii是如何处理的就讲完啦,包括从URL解析,Request和Response组件,Session组件这一整套内容,其实,官网将错误处理,日志系统归类到这章,我感觉这两章对于开发者来讲还是比较重要的,所以,我下面会分两个小章来详细讲讲这两个系统。^_^
【Yii系列】处理请求的更多相关文章
- 【Yii系列】Yii2.0基础框架
缘起 因为一个月的短暂停留,我在给朋友搞事情,所以Yii系列的文章耽搁了很长时间,现在又重拾当时的知识,给大伙好好撸下这一系列的博客 提起Yii,虽然是国外的开发者搞的,但是它的作者是华人,这才是让我 ...
- 【Yii系列】最佳实践之后台业务框架
缘起 上面的几章都讲概念了,没有怎么讲到实践的东西,可能会有些枯燥,这很正常的,概念还是需要慢慢啃的,尤其是官网其他的部分,需要狠狠的啃. 什么,你啃不动了?看看官网旁边的那个在线用户吧. 你不啃的时 ...
- Yii 1.1 请求报400错误
Yii的action可以带参数,比如: class PostController extends CController { public function actionCreate($categor ...
- 【Yii系列】错误处理和日志系统
缘起 跟随上一章的脚步,上一章中,我们主要讲解了在用户发起请求,解析请求,服务器反馈请求以及session的一些知识点,这过程中,难免会遇到一些问题,比方说数据库查询失败,用户输入导致脚本出错,网络问 ...
- OData V4 系列 Ajax请求 CRUD
OData 学习目录 上一篇已经完成了服务创建,本篇主要介绍如何通过Ajax请求Odata服务,OData操作主要有 Get.Post.Patch.Put.Delete等操作. Post 操作 p ...
- [AJAX系列]XMLHttpRequest请求
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Yii系列教程(四):使用Memcache保存会话
1环境准备 安装Memcached服务端: yum -y installmemcached.x86_64 安装PHP-Memcache扩展: yum -y installphp-pecl-memcac ...
- Yii系列教程(三):集成Redis
1安装Redis 切换至/usr/local/src下,下载并安装redis: $ wgethttp://redis.googlecode.com/files/redis-2.6.12.tar.gz ...
- Yii系列总结:yii 标签用法
yii 常用标签:label标签.文本标签.error标签.textarea标签.hidden标签.password标签.url标签.radio标签.file标签.button标签.checkBox标 ...
随机推荐
- Win8下,以管理员身份启动VS项目
之前一直是先以管理员身份启动VS,然后再打开项目的,比较麻烦,找了好久,总算有一个处理方案了 在Windows7下 通常使用修改属性的方式:在任意快捷方式上右击,选择属性,选择高级,选择以管理员身份启 ...
- Windows环境下安装配置Teamcity配合git自动发布mvc,webapi站点
以下是本人配置Teamcity具体环境和步骤,只实现了项目发布,打包.Nodejs npm gulp没有配置成功,后期补上. 1 环境安装 本人使用的是windows7 sp1 64位系统,(.net ...
- 负笈前行--DPDK代码风格
每个公司都会有自己代码风格或者编程规范,都旨在防范编程语言的一些陷阱或者提高代码效率,还有就是保持一致编码风格来提高代码可读性,方便code review: 或者说代码的一种美学,比如python也就 ...
- 【Spring】使用Spring的AbstractRoutingDataSource实现多数据源切换
最近因为项目需要在做两个项目间数据同步的需求,具体是项目1的数据通过消息队列同步到项目2中,因为这个更新操作还涉及到更新多个库的数据,所以就需要多数据源切换的操作.下面就讲讲在Spring中如何进行数 ...
- GET和POST的区别,何时使用POST?
GET:一般用于信息获取,使用URL传递参数,对所发送信息的数量也有限制,一般在2000个字符 POST:一般用于修改服务器上的资源,对所发送的信息没有限制. GET方式需要使用Re ...
- Codeforces 506E Mr. Kitayuta's Gift (矩阵乘法,动态规划)
描述: 给出一个单词,在单词中插入若干字符使其为回文串,求回文串的个数(|s|<=200,n<=10^9) 这道题超神奇,不可多得的一道好题 首先可以搞出一个dp[l][r][i]表示回文 ...
- Spark 键值对RDD操作
键值对的RDD操作与基本RDD操作一样,只是操作的元素由基本类型改为二元组. 概述 键值对RDD是Spark操作中最常用的RDD,它是很多程序的构成要素,因为他们提供了并行操作各个键或跨界点重新进行数 ...
- Matlab命令行编译运行HelloWorld
Matlab安装完成后用记事本写一个文件HelloWorld.m内容如下: function HelloWorld() disp('Hello,World!'); end 保存后在命令行中切到Hell ...
- windows管道
匿名管道的使用 匿名管道主要用于本地父进程和子进程之间的通信, 在父进程中的话,首先是要创建一个匿名管道, 在创建匿名管道成功后,可以获取到对这个匿名管道的读写句柄, 然后父进程就可以向这个匿名管道中 ...
- SaberRD之直流工作点分析
直流工作点分析(DC Operating Point Analysis)用于确定电路的静态工作点. 静态工作点的概念来源于三极管的电流放大特性.三极管放大电路中,当交流输入信号为零时,电路处于直流工作 ...