解读PHP的Yii框架中请求与响应的处理流程


Posted in PHP onMarch 17, 2016

一、请求(Requests)
请求:
一个应用的请求是用 yii\web\Request 对象来表示的,该对象提供了诸如 请求参数(译者注:通常是GET参数或者POST参数)、HTTP头、cookies等信息。 默认情况下,对于一个给定的请求,你可以通过 request application component 应用组件(yii\web\Request 类的实例) 获得访问相应的请求对象。在本章节,我们将介绍怎样在你的应用中使用这个组件。

1.请求参数

要获取请求参数,你可以调用 request 组件的 yii\web\Request::get() 方法和 yii\web\Request::post() 方法。 他们分别返回 $_GET 和$_POST 的值。例如,

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

$get = $request->get(); 
// 等价于: $get = $_GET;

$id = $request->get('id'); 
// 等价于: $id = isset($_GET['id']) ? $_GET['id'] : null;

$id = $request->get('id', 1); 
// 等价于: $id = isset($_GET['id']) ? $_GET['id'] : 1;

$post = $request->post(); 
// 等价于: $post = $_POST;

$name = $request->post('name'); 
// 等价于: $name = isset($_POST['name']) ? $_POST['name'] : null;

$name = $request->post('name', ''); 
// 等价于: $name = isset($_POST['name']) ? $_POST['name'] : '';

信息:建议你像上面那样通过 request 组件来获取请求参数,而不是 直接访问 $_GET 和 $_POST。 这使你更容易编写测试用例,因为你可以伪造数据来创建一个模拟请求组件。
当实现 RESTful APIs 接口的时候,你经常需要获取通过PUT, PATCH或者其他的 request methods 请求方法提交上来的参数。你可以通过调用 yii\web\Request::getBodyParam() 方法来获取这些参数。例如,

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

// 返回所有参数
$params = $request->bodyParams;

// 返回参数 "id"
$param = $request->getBodyParam('id');

信息:不同于 GET 参数,POST,PUT,PATCH 等等这些提交上来的参数是在请求体中被发送的。 当你通过上面介绍的方法访问这些参数的时候,request 组件会解析这些参数。 你可以通过配置 yii\web\Request::parsers 属性来自定义怎样解析这些参数。

2.请求方法

你可以通过 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 */ }

3.请求URLs

request 组件提供了许多方式来检测当前请求的URL。

假设被请求的URL是 http://example.com/admin/index.php/product?id=100, 你可以像下面描述的那样获取URL的各个部分:

  • yii\web\Request::url:返回 /admin/index.php/product?id=100, 此URL不包括host info部分。
  • yii\web\Request::absoluteUrl:返回 http://example.com/admin/index.php/product?id=100, 包含host infode的整个URL。
  • yii\web\Request::hostInfo:返回 http://example.com, 只有host info部分。
  • yii\web\Request::pathInfo:返回 /product, 这个是入口脚本之后,问号之前(查询字符串)的部分。
  • yii\web\Request::queryString:返回 id=100,问号之后的部分。
  • yii\web\Request::baseUrl:返回 /admin, host info之后, 入口脚本之前的部分。
  • yii\web\Request::scriptUrl:返回 /admin/index.php, 没有path info和查询字符串部分。
  • yii\web\Request::serverName:返回 example.com, URL中的host name。
  • yii\web\Request::serverPort:返回 80, 这是web服务中使用的端口。

4.HTTP头

你可以通过 yii\web\Request::headers 属性返回的 yii\web\HeaderCollection 获取HTTP头信息。 例如,

// $headers 是一个 yii\web\HeaderCollection 对象
$headers = Yii::$app->request->headers;

// 返回 Accept header 值
$accept = $headers->get('Accept');

if ($headers->has('User-Agent')) { /* 这是一个 User-Agent 头 */ }

请求组件也提供了支持快速访问常用头的方法,包括:

  • yii\web\Request::userAgent:返回 User-Agent 头。
  • yii\web\Request::contentType:返回 Content-Type 头的值, Content-Type 是请求体中MIME类型数据。
  • yii\web\Request::acceptableContentTypes:返回用户可接受的内容MIME类型。 返回的类型是按照他们的质量得分来排序的。得分最高的类型将被最先返回。
  • yii\web\Request::acceptableLanguages:返回用户可接受的语言。 返回的语言是按照他们的偏好层次来排序的。第一个参数代表最优先的语言。

假如你的应用支持多语言,并且你想在终端用户最喜欢的语言中显示页面,那么你可以使用语言协商方法 yii\web\Request::getPreferredLanguage()。 这个方法通过 yii\web\Request::acceptableLanguages 在你的应用中所支持的语言列表里进行比较筛选,返回最适合的语言。

提示:你也可以使用 yii\filters\ContentNegotiator 过滤器进行动态确定哪些内容类型和语言应该在响应中使用。 这个过滤器实现了上面介绍的内容协商的属性和方法。

5.客户端信息

你可以通过 yii\web\Request::userHost 和 yii\web\Request::userIP 分别获取host name和客户机的IP地址, 例如,

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

二、响应(Responses)
响应:
当应用完成处理一个请求后, 会生成一个yii\web\Response响应对象并发送给终端用户 响应对象包含的信息有HTTP状态码,HTTP头和主体内容等, 网页应用开发的最终目的本质上就是根据不同的请求构建这些响应对象。

在大多是情况下主要处理继承自 yii\web\Response 的 response 应用组件, 尽管如此,Yii也允许你创建你自己的响应对象并发送给终端用户,这方面后续会阐述。

在本节,将会描述如何构建响应和发送给终端用户。

1.状态码

构建响应时,最先应做的是标识请求是否成功处理的状态,可通过设置 yii\web\Response::statusCode 属性,该属性使用一个有效的HTTP 状态码。例如,为标识处理已被处理成功, 可设置状态码为200,如下所示:

Yii::$app->response->statusCode = 200;

尽管如此,大多数情况下不需要明确设置状态码,因为 yii\web\Response::statusCode 状态码默认为200, 如果需要指定请求失败,可抛出对应的HTTP异常,如下所示:

throw new \yii\web\NotFoundHttpException;

当错误处理器 捕获到一个异常,会从异常中提取状态码并赋值到响应, 对于上述的 yii\web\NotFoundHttpException 对应HTTP 404状态码,以下为Yii预定义的HTTP异常:

  • yii\web\BadRequestHttpException: status code 400.
  • yii\web\ConflictHttpException: status code 409.
  • yii\web\ForbiddenHttpException: status code 403.
  • yii\web\GoneHttpException: status code 410.
  • yii\web\MethodNotAllowedHttpException: status code 405.
  • yii\web\NotAcceptableHttpException: status code 406.
  • yii\web\NotFoundHttpException: status code 404.
  • yii\web\ServerErrorHttpException: status code 500.
  • yii\web\TooManyRequestsHttpException: status code 429.
  • yii\web\UnauthorizedHttpException: status code 401.
  • yii\web\UnsupportedMediaTypeHttpException: status code 415.

如果想抛出的异常不在如上列表中,可创建一个yii\web\HttpException异常,带上状态码抛出,如下:

throw new \yii\web\HttpException(402);

2.HTTP 头部

可在 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::send()方法调用前新注册的头信息并不会发送给用户。

3.响应主体

大多是响应应有一个主体存放你想要显示给终端用户的内容。

如果已有格式化好的主体字符串,可赋值到响应的yii\web\Response::content属性,例如:

Yii::$app->response->content = 'hello world!';

如果在发送给终端用户之前需要格式化,应设置 yii\web\Response::format 和 yii\web\Response::data 属性,yii\web\Response::format 属性指定yii\web\Response::data中数据格式化后的样式,例如:

$response = Yii::$app->response;
$response->format = \yii\web\Response::FORMAT_JSON;
$response->data = ['message' => 'hello world'];

Yii支持以下可直接使用的格式,每个实现了yii\web\ResponseFormatterInterface 类, 可自定义这些格式器或通过配置yii\web\Response::formatters 属性来增加格式器。

  • yii\web\Response::FORMAT_HTML: 通过 yii\web\HtmlResponseFormatter 来实现.
  • yii\web\Response::FORMAT_XML: 通过 yii\web\XmlResponseFormatter来实现.
  • yii\web\Response::FORMAT_JSON: 通过 yii\web\JsonResponseFormatter来实现.
  • yii\web\Response::FORMAT_JSONP: 通过 yii\web\JsonResponseFormatter来实现.

上述响应主体可明确地被设置,但是在大多数情况下是通过 操作 方法的返回值隐式地设置,常用场景如下所示:

public function actionIndex()
{
 return $this->render('index');
}

上述的 index 操作返回 index 视图渲染结果,返回值会被 response 组件格式化后发送给终端用户。

因为响应格式默认为yii\web\Response::FORMAT_HTML, 只需要在操作方法中返回一个字符串, 如果想使用其他响应格式,应在返回数据前先设置格式,例如:

public function actionInfo()
{
 \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
 return [
  'message' => 'hello world',
  'code' => 100,
 ];
}

如上所述,触雷使用默认的 response 应用组件,也可创建自己的响应对象并发送给终端用户,可在操作方法中返回该响应对象,如下所示:

public function actionInfo()
{
 return \Yii::createObject([
  'class' => 'yii\web\Response',
  'format' => \yii\web\Response::FORMAT_JSON,
  'data' => [
   'message' => 'hello world',
   'code' => 100,
  ],
 ]);
}

注意: 如果创建你自己的响应对象,将不能在应用配置中设置 response 组件,尽管如此, 可使用 依赖注入 应用通用配置到你新的响应对象。

4.浏览器跳转

浏览器跳转依赖于发送一个Location HTTP 头,因为该功能通常被使用,Yii提供对它提供了特别的支持。

可调用yii\web\Response::redirect() 方法将用户浏览器跳转到一个URL地址,该方法设置合适的 带指定URL的 Location 头并返回它自己为响应对象,在操作的方法中,可调用缩写版yii\web\Controller::redirect(),例如:

public function actionOld()
{
 return $this->redirect('http://example.com/new', 301);
}

在如上代码中,操作的方法返回redirect() 方法的结果,如前所述,操作的方法返回的响应对象会被当总响应发送给终端用户。

除了操作方法外,可直接调用yii\web\Response::redirect() 再调用 yii\web\Response::send() 方法来确保没有其他内容追加到响应中。

\Yii::$app->response->redirect('http://example.com/new', 301)->send();

补充: yii\web\Response::redirect() 方法默认会设置响应状态码为302,该状态码会告诉浏览器请求的资源 临时 放在另一个URI地址上,可传递一个301状态码告知浏览器请求的资源已经 永久 重定向到新的URId地址。
如果当前请求为AJAX 请求,发送一个 Location 头不会自动使浏览器跳转,为解决这个问题, yii\web\Response::redirect() 方法设置一个值为要跳转的URL的X-Redirect 头, 在客户端可编写JavaScript 代码读取该头部值然后让浏览器跳转对应的URL。

补充: Yii 配备了一个yii.js JavaScript 文件提供常用JavaScript功能,包括基于X-Redirect头的浏览器跳转, 因此,如果你使用该JavaScript 文件(通过yii\web\YiiAsset 资源包注册),就不需要编写AJAX跳转的代码。

5.发送文件

和浏览器跳转类似,文件发送是另一个依赖指定HTTP头的功能,Yii提供方法集合来支持各种文件发送需求,它们对HTTP头都有内置的支持。

  • yii\web\Response::sendFile(): 发送一个已存在的文件到客户端
  • yii\web\Response::sendContentAsFile(): 发送一个文本字符串作为文件到客户端
  • yii\web\Response::sendStreamAsFile(): 发送一个已存在的文件流作为文件到客户端

这些方法都将响应对象作为返回值,如果要发送的文件非常大,应考虑使用 yii\web\Response::sendStreamAsFile() 因为它更节约内存,以下示例显示在控制器操作中如何发送文件:

public function actionDownload()
{
 return \Yii::$app->response->sendFile('path/to/file.txt');
}

如果不是在操作方法中调用文件发送方法,在后面还应调用 yii\web\Response::send() 没有其他内容追加到响应中。

\Yii::$app->response->sendFile('path/to/file.txt')->send();

一些浏览器提供特殊的名为X-Sendfile的文件发送功能,原理为将请求跳转到服务器上的文件, Web应用可在服务器发送文件前结束,为使用该功能,可调用yii\web\Response::xSendFile(), 如下简要列出一些常用Web服务器如何启用X-Sendfile 功能:

Apache: X-Sendfile
Lighttpd v1.4: X-LIGHTTPD-send-file
Lighttpd v1.5: X-Sendfile
Nginx: X-Accel-Redirect
Cherokee: X-Sendfile and X-Accel-Redirect

6.发送响应

在yii\web\Response::send() 方法调用前响应中的内容不会发送给用户,该方法默认在yii\base\Application::run() 结尾自动调用,尽管如此,可以明确调用该方法强制立即发送响应。

yii\web\Response::send() 方法使用以下步骤来发送响应:

  • 触发 yii\web\Response::EVENT_BEFORE_SEND 事件.
  • 调用 yii\web\Response::prepare() 来格式化 yii\web\Response::data 为 yii\web\Response::content.
  • 触发 yii\web\Response::EVENT_AFTER_PREPARE 事件.
  • 调用 yii\web\Response::sendHeaders() 来发送注册的HTTP头
  • 调用 yii\web\Response::sendContent() 来发送响应主体内容
  • 触发 yii\web\Response::EVENT_AFTER_SEND 事件.

一旦yii\web\Response::send() 方法被执行后,其他地方调用该方法会被忽略, 这意味着一旦响应发出后,就不能再追加其他内容。

如你所见yii\web\Response::send() 触发了几个实用的事件,通过响应这些事件可调整或包装响应。

PHP 相关文章推荐
如何开发一个虚拟域名系统
Oct 09 PHP
PHP在字符串中查找指定字符串并删除的代码
Oct 02 PHP
php数组函数序列之array_slice() - 在数组中根据条件取出一段值,并返回
Nov 07 PHP
用PHP提取中英文词语以及数字的首字母的方法介绍
Apr 23 PHP
通过php添加xml文档内容的方法
Jan 23 PHP
PHP读取txt文本文件并分页显示的方法
Mar 11 PHP
php自定义类fsocket模拟post或get请求的方法
Jul 31 PHP
PHP设计模式之工厂模式与单例模式
Sep 28 PHP
php对接java现实加签验签的实例
Nov 25 PHP
PHP+MYSQL实现读写分离简单实战
Mar 13 PHP
Yii2使用$this->context获取当前的Module、Controller(控制器)、Action等
Mar 29 PHP
PHP后台备份MySQL数据库的源码实例
Mar 18 PHP
PHP的Yii框架中使用数据库的配置和SQL操作实例教程
Mar 17 #PHP
编写PHP程序检查字符串中的中文字符个数的实例分享
Mar 17 #PHP
实例讲解如何在PHP的Yii框架中进行错误和异常处理
Mar 17 #PHP
解析PHP的Yii框架中cookie和session功能的相关操作
Mar 17 #PHP
简要剖析PHP的Yii框架的组件化机制的基本知识
Mar 17 #PHP
PHP的Yii框架中YiiBase入口类的扩展写法示例
Mar 17 #PHP
Symfony控制层深入详解
Mar 17 #PHP
You might like
一个显示某段时间内每个月的方法 返回由这些月份组成的数组
2012/05/16 PHP
深入PHP异步执行的详解
2013/06/03 PHP
php不使用copy()函数复制文件的方法
2015/03/13 PHP
Thinkphp5行为使用方法汇总
2017/12/21 PHP
Laravel 实现密码重置功能
2018/02/23 PHP
javascript下判断一个元素是否存在的代码
2010/03/05 Javascript
js的.innerHTML = ""IE9下显示有错误的解决方法
2013/09/16 Javascript
常用js字符串判断方法整理
2013/10/18 Javascript
使用CSS样式position:fixed水平滚动的方法
2014/02/19 Javascript
jQuery中$.click()无效问题分析
2015/01/29 Javascript
js实现大转盘抽奖游戏实例
2015/06/24 Javascript
JavaScript仿商城实现图片广告轮播实例代码
2016/02/06 Javascript
两行代码轻松搞定JavaScript日期验证
2016/08/03 Javascript
JS给swf传参数的实现方法
2016/09/13 Javascript
Bootstrap实现带暂停功能的轮播组件(推荐)
2016/11/25 Javascript
简单的渐变轮播插件
2017/01/12 Javascript
快速理解 JavaScript 中的 LHS 和 RHS 查询的用法
2017/08/24 Javascript
Vue隐藏显示、只读实例代码
2018/07/18 Javascript
JSONP原理及应用实例详解
2018/09/13 Javascript
微信小程序实现banner图轮播效果
2020/06/28 Javascript
Node.js API详解之 repl模块用法实例分析
2020/05/25 Javascript
在SAE上部署Python的Django框架的一些问题汇总
2015/05/30 Python
利用Python实现图书超期提醒
2016/08/02 Python
利用PyInstaller将python程序.py转为.exe的方法详解
2017/05/03 Python
python 使用装饰器并记录log的示例代码
2019/07/12 Python
详解HTML5 LocalStorage 本地存储
2016/12/23 HTML / CSS
美国鞋类购物网站:Shiekh Shoes
2016/08/21 全球购物
2013年高中生自我评价
2013/10/23 职场文书
毕业生自荐书
2014/02/02 职场文书
优秀学生获奖感言
2014/02/15 职场文书
《望洞庭》教学反思
2014/02/16 职场文书
周年庆典主持词
2014/04/02 职场文书
工伤事故赔偿协议书(标准)
2014/09/29 职场文书
扶贫办主任查摆“四风”问题个人对照检查材料思想汇报
2014/10/02 职场文书
高中议论文(范文2篇)
2019/08/19 职场文书
导游词之珠海轮廓
2019/10/25 职场文书