【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标 ...
随机推荐
- asp.net core mvc剖析:KestrelServer
KestrelServer是基于Libuv开发的高性能web服务器,那我们现在就来看一下它是如何工作的.在上一篇文章中提到了Program的Main方法,在这个方法里Build了一个WebHost,我 ...
- 11 Python+selenium对下拉框(select)进行处理
[环境信息] Python3.4+IE+windows2008 [Select下拉框处理] 1.对于如图1的下拉框,可以用selenium自带的Select类进行选择. 2.定位示例: from se ...
- 关于Android中为什么主线程不会因为Looper.loop()里的死循环卡死?引发的思考,事实可能不是一个 epoll 那么 简单。
( 转载请务必标明出处:http://www.cnblogs.com/linguanh/, 本文出自:[林冠宏(指尖下的幽灵)的博客]) 前序 本文将会把一下三个问题阐述清楚以及一个网上的普遍观点的补 ...
- mongodb 安装到创建用户,认证auth,httpinterface
今天花了一天时间来解开这个mongodb的谜团,如果有遇到了其他的问题,可以咨询我. #开始 2.6.10安装方式 不同版本后面设置用户权限方式有所差异#下载这个版本的mongodb mongodb- ...
- linux下zookeeper 配置参数
-----------zookeeper 配置文件 clientPort ---服务的监听端口dataDir ---用于存放内存数据库快照的文件夹,同时用于集群的myid文件也存在这个文件夹里 (注意 ...
- 性能测试工具 - Apache JMeter (安装)
简介 Apache JMeter 是100%纯java语言开发的负载测试和性能测试开源工具. 功能 Apache JMeter可以对静态/动态资源进行性能测试,模拟多个用户并行请求资源端,以测试其强度 ...
- 了解 : Odata 的 $filter
api/jobPosts?$filter=company/name eq "string" //基本 api/orders?$filter=orderItem/product/EF ...
- EntityFrameworkCore使用Migrations自动更新数据库
EntityFrameworkCore使用Migrations自动更新数据库 系统环境:Win10 IDE:VS2017 RC4 .netcore版本:1.1 一.新建ASP.NET Core Web ...
- Java调度框架Quartz简单示例
Quartz的大名如雷贯耳,这里就不赘述,而且本文也不作为深入探讨,只是看完Quartz的官方文档后,下个简单示例,至少证明曾经花了点时间学习过,以备不时之需. Quartz使用了SLF4J,所以至少 ...
- 关于Edittext默认弹出软键盘为数字键
如果说我们只是输入数字的话,我们可以直接在xml文件中: android:inputType="number" 如果是身份证类型的话,我们可以这样: android:inputTy ...