Yii 的修行之路 - HTTP的请求与响应

简述

HTTP交互主要分为 request(请求) 和 response(响应) 两种方式。

  1. 对于Yii2来说,HTTP的request请求是用 YII::$app->request 来表示的对象,这是Yii提供的处理HTTP的request请求的插件,里面有很多实用的功能代码:
request;        if ($request->isGet) {            echo $request->get('id', 'get获取的值为空');        } else if ($request->isPost) {            $request->post('name', 'post传递的值为空');        }           echo $request->userIp; // 获取当前IP地址?>
  1. 对于Yii2来说,HTTP的response请求是用 YII::$app->response 来表示的对象,这是Yii提供的处理HTTP的response请求的插件,里面与很多实用的功能代码:
response;        // 设置服务器返回的状态码设置        $response->statusCode = '404';        // 设置pragma的设置,提醒浏览器不设置缓存        $response->headers->add('pragma', 'no-chche');        // 设置pragma的设置,提醒浏览器设置缓存5秒        $response->headers->set('pragma', 'max-age=5');        $response->headers->remove('pragma');        // 设置页面跳转到指定路径        $response->headers->add('location', 'https://rongx.github.io');        // 设置页面跳转,状态码设置为302(调用Controller里面的方法)        $this->redirect('https://rongx.github.io', 302);        // 设置文件下载链接        $response->headers->add('content-dispostion', 'attachment; filename="a.jpg"');        // 提供文件下载,文件路径实相对与入口文件来说的        $response->sendFile('./b.jpg');?>
  1. 其实在 yiiwebController 里面也有很多处理HTTP的小插件功能代码。

请求参数(request)

要获取请求参数,你可以调用 request 组件的 yiiwebRequest::get() 方法和 yiiwebRequest::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'] : '';
  1. 信息:建议你像上面那样通过 request 组件来获取请求参数,而不是 直接访问 $_GET 和 $_POST。

  2. 信息:这使你更容易编写测试用例,因为你可以伪造数据来创建一个模拟请求组件。

当实现 RESTful APIs 接口的时候,你经常需要获取通过PUT, PATCH或者其他的 request methods 请求方法提交上来的参数。

你可以通过调用 yiiwebRequest::getBodyParam() 方法来获取这些参数。例如:

$request = Yii::$app->request; // 返回所有参数 $params = $request->bodyParams; // 返回参数 "id"$param = $request->getBodyParam('id');
  1. 信息:不同于 GET 参数,POST,PUT,PATCH 等等这些提交上来的参数是在请求体中被发送的。

  2. 信息:当你通过上面介绍的方法访问这些参数的时候,request 组件会解析这些参数。

  3. 信息:你可以通过配置 yiiwebRequest::parsers 属性来自定义怎样解析这些参数。

请求方法(request)

你可以通过 request 表达式来获取当前请求使用的HTTP方法。

这里还提供了一整套布尔属性用于检测当前请求是某种类型。例如:

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

请求URLs(request)

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

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

  1. yiiwebRequest::url:返回 /admin/index.php/product?id=100, 此URL不包括host info部分。

  2. yiiwebRequest::absoluteUrl:返回 http://example.com/admin/index.php/product?id=100, 包含host infode的整个URL。

  3. yiiwebRequest::hostInfo:返回 http://example.com, 只有host info部分。

  4. yiiwebRequest::pathInfo:返回 /product, 这个是入口脚本之后,问号之前(查询字符串)的部分。

  5. yiiwebRequest::queryString:返回 id=100,问号之后的部分。

  6. yiiwebRequest::baseUrl:返回 /admin, host info之后, 入口脚本之前的部分。

  7. yiiwebRequest::scriptUrl:返回 /admin/index.php, 没有path info和查询字符串部分。

  8. yiiwebRequest::serverName:返回 example.com, URL中的host name。

  9. yiiwebRequest::serverPort:返回 80, 这是web服务中使用的端口。

HTTP头(request)

你可以通过 yiiwebRequest::headers 属性返回的 yiiwebHeaderCollection 获取HTTP头信息。 例如:

// $headers 是一个 yii\web\HeaderCollection 对象$headers = Yii::$app->request->headers;// 返回 Accept header 值$accept = $headers->get('Accept');if ($headers->has('User-Agent')) { /* 这是一个 User-Agent 头 */ }

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

  1. yiiwebRequest::userAgent:返回 User-Agent 头。

  2. yiiwebRequest::contentType:返回 Content-Type 头的值, Content-Type 是请求体中MIME类型数据。

  3. yiiwebRequest::acceptableContentTypes:返回用户可接受的内容MIME类型。 返回的类型是按照他们的质量得分来排序的。得分最高的类型将被最先返回。

  4. yiiwebRequest::acceptableLanguages:返回用户可接受的语言。 返回的语言是按照他们的偏好层次来排序的。第一个参数代表最优先的语言。

假如你的应用支持多语言,并且你想在终端用户最喜欢的语言中显示页面,那么你可以使用语言协商方法 yiiwebRequest::getPreferredLanguage()。

这个方法通过 yiiwebRequest::acceptableLanguages 在你的应用中所支持的语言列表里进行比较筛选,返回最适合的语言。

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

客户端信息(request)

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

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

响应(response)

当应用完成处理一个请求后, 会生成一个yiiwebResponse响应对象并发送给终端用户 响应对象包含的信息有HTTP状态码,HTTP头和主体内容等。

网页应用开发的最终目的本质上就是根据不同的请求构建这些响应对象。

在大多是情况下主要处理继承自 yiiwebResponse 的 response 应用组件, 尽管如此,Yii也允许你创建你自己的响应对象并发送给终端用户。

状态码

构建响应时,最先应做的是标识请求是否成功处理的状态,可通过设置 yiiwebResponse::statusCode 属性,该属性使用一个有效的HTTP 状态码。

例如,为标识处理已被处理成功, 可设置状态码为200,如下所示:

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

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

throw new \yii\web\NotFoundHttpException;

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

  1. yiiwebBadRequestHttpException: status code 400.

  2. yiiwebConflictHttpException: status code 409.

  3. yiiwebForbiddenHttpException: status code 403.

  4. yiiwebGoneHttpException: status code 410.

  5. yiiwebMethodNotAllowedHttpException: status code 405.

  6. yiiwebNotAcceptableHttpException: status code 406.

  7. yiiwebNotFoundHttpException: status code 404.

  8. yiiwebServerErrorHttpException: status code 500.

  9. yiiwebTooManyRequestsHttpException: status code 429.

  10. yiiwebUnauthorizedHttpException: status code 401.

  11. yiiwebUnsupportedMediaTypeHttpException: status code 415.

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

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

HTTP 头部

可在 response 组件中操控yiiwebResponse::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');
  1. 补充: 头名称是大小写敏感的,在yiiwebResponse::send()方法调用前新注册的头信息并不会发送给用户。

响应主体

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

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

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

如果在发送给终端用户之前需要格式化,应设置 yiiwebResponse::format 和 yiiwebResponse::data 属性。

yiiwebResponse::format 属性指定 yiiwebResponse::data 中数据格式化后的样式,例如:

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

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

  1. yiiwebResponse::FORMAT_HTML: 通过 yiiwebHtmlResponseFormatter 来实现.

  2. yiiwebResponse::FORMAT_XML: 通过 yiiwebXmlResponseFormatter来实现.

  3. yiiwebResponse::FORMAT_JSON: 通过 yiiwebJsonResponseFormatter来实现.

  4. yiiwebResponse::FORMAT_JSONP: 通过 yiiwebJsonResponseFormatter来实现.

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

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

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

因为响应格式默认为 yiiwebResponse::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, ],     ]); }
  1. 注意: 如果创建你自己的响应对象,将不能在应用配置中设置 response 组件,尽管如此, 可使用 依赖注入应用通用配置到你新的响应对象。

浏览器跳转

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

可调用 yiiwebResponse::redirect() 方法将用户浏览器跳转到一个URL地址。

该方法设置合适的 带指定URL的 Location 头并返回它自己为响应对象,在操作的方法中,可调用缩写版 yiiwebController::redirect(),例如:

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

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

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

\Yii::$app->response->redirect('http://example.com/new', 301)->send();
  1. 补充: yiiwebResponse::redirect() 方法默认会设置响应状态码为302,该状态码会告诉浏览器请求的资源 临时 放在另一个URI地址上,可传递一个301状态码告知浏览器请求的资源已经 永久 重定向到新的URId地址。

如果当前请求为AJAX 请求,发送一个 Location 头不会自动使浏览器跳转,为解决这个问题, yiiwebResponse::redirect() 方法设置一个值为要跳转的URL的X-Redirect 头, 在客户端可编写JavaScript 代码读取该头部值然后让浏览器跳转对应的URL。

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

发送文件

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

  1. yiiwebResponse::sendFile(): 发送一个已存在的文件到客户端

  2. yiiwebResponse::sendContentAsFile(): 发送一个文本字符串作为文件到客户端

  3. yiiwebResponse::sendStreamAsFile(): 发送一个已存在的文件流作为文件到客户端

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

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

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

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

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

  1. Apache: X-Sendfile

  2. Lighttpd v1.4: X-LIGHTTPD-send-file

  3. Lighttpd v1.5: X-Sendfile

  4. Nginx: X-Accel-Redirect

  5. Cherokee: X-Sendfile and X-Accel-Redirect

发送响应

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

yiiwebResponse::send() 方法使用以下步骤来发送响应:

  1. 触发 yiiwebResponse::EVENT_BEFORE_SEND 事件.

  2. 调用 yiiwebResponse::prepare() 来格式化 yiiwebResponse::data 为yiiwebResponse::content.

  3. 触发 yiiwebResponse::EVENT_AFTER_PREPARE 事件.

  4. 调用 yiiwebResponse::sendHeaders() 来发送注册的HTTP头

  5. 调用 yiiwebResponse::sendContent() 来发送响应主体内容

  6. 触发 yiiwebResponse::EVENT_AFTER_SEND 事件.

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

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

关键字:php, yii

版权声明

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处。如若内容有涉嫌抄袭侵权/违法违规/事实不符,请点击 举报 进行投诉反馈!

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部