CakePHP 2.x CookBook 中文版 第五章 控制器 之 请求和响应对象

请求和响应对象

在 CakePHP 2.0 中请求和响应对象是新的。在之前的版本中,这些对象是用数组来表示的,相关的方法分散在RequestHandlerComponentRouter、 Dispatcher 和 Controller 中。请求包含的信息上没有认证对象。在 2.0 中,CakeRequest 和 CakeResponse 用于此目的。

CakeRequest

CakeRequest 是 CakePHP 中的默认请求对象。它在请求数据中集成了一些咨询和交互特性。 CakeReqeust 建立在每个请求上,并以引用方式传递给使用请求数据的各个层。默认情况下,CakeRequest 赋值给 $this->request,并且在控制器、视图和助手中可用。 CakeRequest 执行的部分职责包括:

  • 将 GET、POST 和 FILES 数组处理成你熟悉的数据结构。
  • 为请求提供相关的自省环境。类似于头信息传送、客户端 IP 地址、运行应用程序的服务器的子域/域信息等。
  • 提供以数组成员/对象属性两种形式访问请求参数。

访问请求对象

CakeRequest 提供了数种访问请求参数的接口。一是使用对象属性,二是使用数组索引,三是通过 $this->request->params

$this->request->controller; $this->request['controller']; $this->request->params['controller'];

上面的所有形式都访问同一个值。提供参数的多种访问形式是为了已有应用程序的迁移。所有的 路由元素 都可以通过这种形式访问。

除了 路由元素 ,还经常需要访问 传递参数 和 命名参数。这此在请求对象中也都可用:

// 传递的参数 $this->request->pass; $this->request['pass']; $this->request->params['pass']; // 命名的参数 $this->request->named; $this->request['named']; $this->request->params['named'];

上面提供了传递参数和命名参数的访问。这里有几个 CakePHP 内部使用的重要/有用的参数,这些参数也能在请求参数中找到:

  • plugin 处理请求的插件,没有插件时为空(null)。
  • controller 处理当前请求的控制器。
  • action 处理当前请求的动作。
  • prefix 当前动作的前缀。更多信息参见 前置路由 。
  • bare 在请求来自 requestAction() 并且包含 bare 选项时出现。Bare 请求没有要渲染的布局。
  • requested 当动作来自 requestAction 时出现并被设置为 true。

访问 query 参数

query 参数可以用 CakeRequest::$query 读取:

// url 为 /posts/index?page=1&sort=title $this->request->query['page']; // 也可以用数组形式来访问 $this->request['url']['page']; // BC 存取器,将在未来版本中被废弃

你也可以直接访问 query 属性,或者以容错方式使用 CakeRequest::query() 读取 url query 数组。 任何不存在的键都返回空(null)值:

$foo = $this->request->query('value_that_does_not_exist'); // $foo === null

访问 POST 数据

所有的 POST 数据都可以用 CakeRequest::$data 访问。包含 data 前缀的任意数据,其数据前缀都会被删除。例如:

// An input with a name attribute equal to 'data[MyModel][title]' is accessible at $this->request->data['MyModel']['title'];

你也可以直接访问 data 属性,或者以容错方式使用 CakeRequest::data() 读取 data 数组。 任何不存在的键都返回空(null)值:

$foo = $this->request->data('Value.that.does.not.exist'); // $foo == null

访问 PUT 或者 POST 数据

2.2 新版功能.

在创建 REST 服务时,常常在 PUT 和 DELETE 接受请求数据。2.2 版中,所有 application/x-www-form-urlencoded请求的主体数据将自动解析并且设置为 $this->data 的 PUT 和 DELETE 请求。如果你接收 JSON 或者 XML 数据,参看后文中如何访问这些请求的部分。

访问 XML 或者 JSON 数据

应用程序经常使用 REST 非 URL 编码的 post 体之间交换数据。你可以以任意格式使用 CakeRequest::input() 访问 input 数据。 通过提供解码功能,你可以接受反序列化形式的内容:

// 获取提交给 PUT/POST 动作的 JOSN 编码数据 $data = $this->request->input('json_decode');

自从某些反序列化方法在被调用时包含附加参数, CakeRequest::input() 也支持在附加参数中传递 json_decode 中的 ‘as array’ 参数,或者如果你想将 XML 转换成 DOM 文档对象:

// Get Xml encoded data submitted to a PUT/POST action $data = $this->request->input('Xml::build', array('return' => 'domdocument'));

访问路径信息

CakeRequest 还提供关于应用程序路径的有用信息。 CakeRequest::$base 和 CakeRequest::$webroot 用于生成 url,并确定应用程序是否在一个子文件夹内。

检查请求

以前使用 RequestHandlerComponent 检测各种请求条件。这些方法已经被移到 CakeRequest,并且新的接口(包含多个与回调兼容的用法):

$this->request->is('post'); $this->request->isPost();

这些方法调用返回相同的值。眼下 RequestHandler 中的方法仍然可用,但是已经被废弃并可能会在后续版本中删除。你也可以轻易地扩展有效的请求探察器,使用 CakeRequest::addDetector() 创建新类型的探察器。你可以建立四种不同类型的探察器:

  • 环境值对比 - 一个环境值对比,对比来自 env() 的值和环境变量中已知的值是否来检测所提供的值是否相等。
  • 模式值对比 - 模式值对比允许你对比来自 env() 的值和正则表达式。
  • 基于选项的对比 - 基于选项的对比使用一个选项列表建立一个正则表达式。后续调用加入一个已经定义选项的探察器来合并选项。
  • 回调探察器 - 回调发现都允许你提供一个 ‘回调’ 类型来处理检测。回调将接受请求对象作为它的唯一参数。

这有一些例子:

// 添加环境探察器。 $this->request->addDetector('post', array('env' => 'REQUEST_METHOD', 'value' => 'POST')); // 添加模式值探察器。 $this->request->addDetector('iphone', array('env' => 'HTTP_USER_AGENT', 'pattern' => '/iPhone/i')); // 添加选项探察器。 $this->request->addDetector('internalIp', array( 'env' => 'CLIENT_IP', 'options' => array('192.168.0.101', '192.168.0.100') )); // 添加回调探察器。可以是匿名函数或者定时调用。 $this->request->addDetector('awesome', array('callback' => function ($request) { return isset($request->awesome); }));

CakeRequest 还包括 CakeRequest::domain()、 CakeRequest::subdomains() 和 CakeRequest::host() 等方法用来帮助带有子域的应用程序,让生命更简单。

下面是一些可用的内置探察器:

  • is('get') 检测当前的请求是否是一个 GET。
  • is('put') 检测当前的请求是否是一个 PUT.
  • is('post') 检测当前的请求是否是一个 POST.
  • is('delete') 检测当前的请求是否是一个 DELETE.
  • is('head') 检测当前的请求是否是一个 HEAD.
  • is('options') C检测当前的请求是否是 OPTIONS.
  • is('ajax') 检测当前的请求是否是来自 X-Requested-with = XmlHttpRequest.
  • is('ssl') 检测当前的请求是否经过 SSL
  • is('flash') 检测当前的请求是否有一个 Flash 用户代理
  • is('mobile') 检测当前的请求是否来自移动代理。

CakeRequest 和 RequestHandlerComponent

由于``CakeRequest`` 提供的过去用于 RequestHandlerComponent 的一些特性,被要求反思如何仍然适合图片。对于 2.0, RequestHandlerComponent 扮演了一个甜心爹爹的角色。在 CakeRequest 提供的功能的顶层挂了一层糖。所谓的糖就是在 RequestHandlerComponent 范围内,基于内容的类型或者 ajax 切换布局和视图。这种分隔两类的工具和糖,让你更易于区分和选择你想要的和你需要的。

与其它层面的请求互动

你可以使用 CakeRequest 反观关于请求的各种事物。除了探察器,你还能找到来自各种属性和方法的其他信息。

  • $this->request->webroot 表示 web 根目录。
  • $this->request->base 表示基目录。
  • $this->request->here 表示当前请求的完整地址。
  • $this->request->query 包含 query 字符串参数。

CakeRequest API

class CakeRequest

CakeRequest 压制 request 参数句柄和反视。

CakeRequest::domain( $tldLength = 1)

返回正在运行的应用程序的域。

CakeRequest::subdomains( $tldLength = 1)

以数组形式返回应用程序的子域。

CakeRequest::host()

返回应用程序的主机。

CakeRequest::method()

返回请求所用的 HTTP 方式。

CakeRequest::onlyAllow( $methods)

设 置允许的 HTTP 方式,如果不匹配则抛出 MethodNotAllowexException。 这个伴随传送方法的 405 输出包含必须的 ‘Allow’ 头。 The 405 response will include the required ‘Allow’ header with the passed methods

2.3 新版功能.

CakeRequest::referer( $local = false)

返回请求的来源地址。

CakeRequest::clientIp( $safe = true)

返回当前访问者的 IP 地址。

CakeRequest::header( $name)

允许你使用此请求访问任意的 HTTP_* 头。

$this->request->header('User-Agent');

将返回此请求使用的用户代理。

CakeRequest::input( $callback [$options ])

从请求中获取 input 数据,并且可以向其传递一个解码函数。附加的参数中的解码函数可以被作为传递给 input()。

CakeRequest::data( $name)

提供 点表示法 访问请求数据。允许读取和修改请求数据,调用也可以串联在一起:

// 修改部分请求数据,以使你能够预一些表单域。 $this->request->data('Post.title', 'New post') ->data('Comment.1.author', 'Mark'); // 你也能读出数据。 $value = $this->request->data('Post.title');
CakeRequest::query( $name)

提供 点表示法 访问 url query 数据:

// url 为 /posts/index?page=1&sort=title $value = $this->request->query('page');

2.3 新版功能.

CakeRequest::is( $type)

检查一个请求是否符合某个标准。应用内置的匹配规则或者通过 CakeRequest::addDetector() 定义的任意附加规则。

CakeRequest::addDetector( $name$options)

添加一个用于 is() 的探察器。更多信息参见 检查请求 。

CakeRequest::accepts( $type = null)

找出客户端接受的内容类型,或者检测它们接受的特定内容的类型。

获取全部类型

$this->request->accepts();

检查单个类型

$this->request->accepts('application/json');
static CakeRequest::acceptLanguage( $language = null)

获取客户端期望的全部语言,或者检测被接受的一个特定语言。

获取被接受的语言的列表

CakeRequest::acceptLanguage();

检测一个指定的被接受的语言

CakeRequest::acceptLanguage('es-es');
property CakeRequest::$data

POST 数据的数组。作为防止错误提示的一种方式,你可以使用 CakeRequest::data() 读取这个属性。

property CakeRequest::$query

query 字符串参数数组。

property CakeRequest::$params

路由元素和请求参数数组。

property CakeRequest::$here

返回当前请求的 uri。

property CakeRequest::$base

应用程序的基路径,通常是 /,除非你的应用程序放在子目录中。

property CakeRequest::$webroot

当前 web 根目录

CakeResponse

CakeResponse 是 CakePHP 中的默认响应。它包含一些在应用程序中生成 HTTP 响应的特性和功能。它也有助于测试,因为它能被 mock/stub以允许你检查将要传输的头信息。 与 CakeRequest 类似, CakeResponse 整合了一些原来放在 ControllerRequestHandlerComponent 和 Dispatcher 中的方法。那些旧方法已经被废弃,转而使用CakeResponse.。 CakeResponse 提供了一个接口,这个接口打包了与下面的任务相关的公用响应:

  • 为跳转传送头信息。
  • 传送内容类型头信息。
  • 传送信息头信息。
  • 传送响应内容。

变化中的响应类

CakePHP 默认使用 CakeResponse。 CakeResponse 对于使用类是灵活透明的。不过如果你需要在应用程序的特定类中替换它,你使用自己的类覆盖或替换 CakeResponse 。通过替换在 index.php 中使用的 CakePHPResponse。

这会使你的应用程序中的所有控制器都用 CustomResponse 代替 CakeResponse。 你也可以在控制器中设置 $this->response 来替换 response 实例。 在测试过程中覆盖这个 response 对象很有用,它允许你 stub 与 header() 一起工作的方法。 更多的信息请参见 CakeResponse 与测试 一节。

处理内容类型

你可以使用 CakeResponse::type() 控制应用程序响应的 内容类型(Content-Type)。如果你的应用程序需要处理没有内置在 CakeResponse 中的内容类型,你还可以使用 type() 映射它们:

// 添加 vCard 类型 $this->response->type(array('vcf' => 'text/v-card')); // 将响应的 Content-Type 设置为 vcard. $this->response->type('vcf');

通常你将会在控制器的 beforeFilter 回调中映射附加的内容类型,如果需要,你可以利用 RequestHandlerComponent的自动视图切换功能。

传输文件

有时候你需要为请求发送文件作为对其的响应。 在 2.3 版之前你可以使用 媒体视图 来实现。 在 2.3 版,MediaView 被废弃,现在你可以使用 CakeResponse::file() 传送文件来响应:

public function sendFile($id) { $file = $this->Attachment->getFile($id); $this->response->file($file['path']); }

上面显示的例子要求向方法传送文件路径。 如果它是列在 CakeReponse::$_mimeTypes 中的已知文件类型,Cake 将发送适当的内容类型头信息。你也可以在调用 CakeResponse::file() 之前使用 CakeResponse::type() 方法添加新的文件类型。

如果你只想让文件显示在浏览器中,禁止文件被下载,可以指定如下选项:

$this->response->file($file['path'], array('download' => true, 'name' => 'foo'));

设置 header

设置 headers 使用的是 CakeResponse::header() 方法。 它能以不同的参数配置调用:

// 设置单个 header $this->response->header('Location', 'http://example.com'); // 设置多个 headers $this->response->header(array('Location' => 'http://example.com', 'X-Extra' => 'My header')); $this->response->header(array('WWW-Authenticate: Negotiate', 'Content-type: application/pdf'));

一个 header 被设置多次,将只保留最后一次的设置,就像普通的 header 调用一样。当调用CakeResponse::header() 时,Header 并没有发送。它们在响应被真实的发送之前只是被缓冲了。

与浏览器缓存交互

有时你需要禁止浏览器缓存控制器动作的结果。 CakeResponse::disableCache() 就是用来干这个的::

public function index() { // do something. $this->response->disableCache(); }

警告

在 SSL 域中使用与下载文件一起使用 disableCache() 传送文件到 Internet Explorer 可能引起错误。

你也可以使用 CakeResponse::cache() 告诉客户端你想要它们缓存响应:

public function index() { //do something $this->response->cache('-1 minute', '+5 days'); }

上面的代码告诉客户端缓存响应结果5天,希望提高访客在速度方面的体验。cache() 的第一个参数是最后编辑(Last-Modifield)的值。过期(Expires)和最大年龄(Max-age)参数是以秒为单位的。还有,缓存控制(Cache-Control)设置为 public。

HTTP 缓存微调

提高应用程序的访问速度的一个又好又容易的办法是使用 HTTP 缓存。 在这种模式下,你只需要通过设置几个 header (如编辑时间、响应实体及其它)来帮助客户端决定是否使用响应的缓存副本。

反对使用代码逻辑处理 缓存和在数据改变时失效(刷新),HTTP 的过期和校验两种模式常常比自己管理缓存更简单。

除了 CakeResponse::cache() ,你还可以使用另外一些方法实现 HTTP 缓存头来利用浏览器或反向代理缓存。

缓存控制 头信息

2.1 新版功能.

在过期模式下,header 包括多个能够使浏览器或代理使用缓存内容的标志。一个 Cache-Control header 如下:

Cache-Control: private, max-age=3600, must-revalidate

CakeResponse 类及一些产生最终有效的 Cache-Control header 工具方法帮助你设置这些头信息。其中的第一个是CakeResponse::sharable() 方法, 它标志一个响应是否考虑在不同的用户或者客户端共享。 此方法实际控制 header 的 public 或者 private 部分。将响应设置成 private 标志着它的全部或部分是设计给单个用户的。要共享缓存需要将 control 指令设置为 public。

此方法的第二个参数用于指定缓存的 Max-age ,它声明响应在多少秒后过期。

public function view() { ... // set the Cache-Control as public for 3600 seconds $this->response->sharable(true, 3600); } public function my_data() { ... // set the Cache-Control as private for 3600 seconds $this->response->sharable(false, 3600); }

CakeResponse 公开了设定 Cache-Control header 中的每个组件的独立方法。

过期头信息

2.1 新版功能.

还是在过期模式下,你可以设置 Expires header,它指定响应在指定的日期/时间之后过期,时间和日期为 HTTP 规范格式。这个 header 可以使用 CakeResponse::expires() 方法设置:

public function view() { $this->response->expires('+5 days'); }

这个方法还接收一个 DateTime 或者能够被 DateTime 类解析的任意字符串。

Etag 头信息

2.1 新版功能.

HTTP 中的缓存校验通常用于内容不间断地更新的情况下,并且仅在缓存过期时通知应用程序生成响应内容。在这种模式下,客户端持续在缓存中存储页面,但是不直接使用它,而是每次向应用程序咨询资源是否被改变。它通常用于图片或者其它静态资源。

Etag header (调用实体 tag)是请求资源的唯一标识字符串。它非常像文件的检验和,缓存比较校验和,确定它们是否匹配。

要想享受到使用这个 header 的好处,你还必须手动调用 CakeResponse::checkNotModified() 方法,或者在应用程序中包含 RequestHandlerComponent

public function index() { $articles = $this->Article->find('all'); $this->response->etag($this->Article->generateHash($articles)); if ($this->response->checkNotModified($this->request)) { return $this->response; } ... }

最后编辑头信息

2.1 新版功能.

还是在 HTTP 缓存校验模式下,你能够设置 Last-Modified header 来标志一个资源最后一次被修改的日期和时间。设置这个 header 帮助 CakePHP 回复缓存客户端,基于客户端缓存的响应是否被编辑了。

要享受到使用这个 header 的好处,你还必须手动调用 CakeResponse::checkNotModified() 方法,或者在应用程序中包含 RequestHandlerComponent ::

public function view() { $article = $this->Article->find('first'); $this->response->modified($article['Article']['modified']); if ($this->response->checkNotModified($this->request)) { return $this->response; } ... }

Vary 头信息

某些情况下,你可能需要使用相同的 url 提供不同的内容。 通常是由于你需要根据浏览器请求的资源的不同为其准备多个页面或者不同的 HTML 回应。你可以使用 Vary header 来应对这种情况:

$this->response->vary('User-Agent'); $this->response->vary('Accept-Encoding', 'User-Agent'); $this->response->vary('Accept-Language');

CakeResponse 与测试

CakeResponse 的最大的胜利也许是来自它使测试控制器和组件变得更容易了。代替遍布多个对象的方法,仅使用一个 mock 对象作为委托给控制器和组件的 CakeResponse 。这会帮助你走近 ‘unit’ 测试并使测试控制器更容易:

public function testSomething() { $this->controller->response = $this->getMock('CakeResponse'); $this->controller->response->expects($this->once())->method('header'); // ... }

除此之外,从命令行运行测试也变得更容易了,你可以使用 mock 避开由于试图在 CLI 中传送 header 引起的 ‘headers sent’ 错误。

CakeResponse API

class CakeResponse

CakeResponse 提供了一些与你传送给客户端的响应交互的有用的方法。

CakeResponse::header( $header = null$value = null)

允许你直接设置一个或多个与响应同时传送的 header。

CakeResponse::charset( $charset = null)

设置在响应中使用的字符集。

CakeResponse::type( $contentType = null)

设置响应的内容类型。你可以使用已知类型的别名或者类型的命名。

CakeResponse::cache( $since$time = '+1 day')

允许你在响应中设置 cache header。

CakeResponse::disableCache()

设置这个 header 将禁止客户端缓存响应。

CakeResponse::sharable( $public = null$time = null)

将 Cache-Control header 设置成 public 或者 private ,选择性的设置资源的 max-age 指令。

2.1 新版功能.

CakeResponse::expires( $time = null)

允许你将 Expires header 设置为确切日期。

2.1 新版功能.

CakeResponse::etag( $tag = null$weak = false)

为响应资源设置唯一的 Etag 标识。

2.1 新版功能.

CakeResponse::modified( $time = null)

以正确的格式将 Last-Modified header 设置为一个确切日期。

2.1 新版功能.

CakeResponse::checkNotModified( CakeRequest $request)

比较请求对象的 cache header 和 响应的 cache header,确定其是否仍然有效。如果仍然有效,则删除所有响应内容,并传送 304 Not Modified header。

2.1 新版功能.

CakeResponse::compress()

为请求撕开 gzip 压缩。

CakeResponse::download( $filename)

允许你以附件的形式传送响应,并设置文件名。

CakeResponse::statusCode( $code = null)

允许你设置响应的状态码。

CakeResponse::body( $content = null)

为响应设置内容主体。

CakeResponse::send()

一旦你建立了响应,调用 send() 将传送全部 heaer 和主体内容。在每个请求中,这是通过 Dispatcher 自动完成的。

CakeResponse::file( $path$options = array())

允许你为显示或下载设置一个文件。

2.3 新版功能.

你可能感兴趣的:(CakePHP 2.x CookBook 中文版 第五章 控制器 之 请求和响应对象)