# 请求对象

## 介绍

请求对象是在前端控制器，路由器，分发器，以及控制类间传递的简单值对象。请求对象封装了请求的模块，控制器，动作以及可选的参数，还包括其他的请求环境，如HTTP，CLI，PHP-GTK。

- 模块名可通过`getModuleName()`和`setModuleName()`访问。

- 控制器名可通过`getControllerName()`和`setControllerName()`访问。

- 控制器调用的动作名称可通过`getActionName()`和`setActionName()`访问。

- 可访问的参数是一个键值对的关联数组。数组可通过`getParams()`和 `setParams()`获取及设置，单个参数可以通过 `getParam()` 和 `setParam()`获取及设置。

基于请求的类型存在更多的可用方法。默认的`Zend_Controller_Request_Http`请求对象，拥有访问请求url、路径信息、`$_GET` 和 `$_POST`参数的方法等等。

请求对象先被传入到前端控制器。如果没有提供请求对象，它将在分发过程的开始、任何路由过程发生之前实例化。请求对象将被传递到分发链中的每个对象。

而且，请求对象在测试中是很有用的。开发人员可根据需要搭建请求环境，包括模块、控制器、动作、参数、URI等等，并且将其传入前端控制器来测试程序流向。如果与响应对象配合，可以对MVC程序进行精确巧妙的单元测试(`unit testing`)。

## HTTP 请求

### 访问请求数据

`Zend_Controller_Request_Http`封装了对相关值的访问，如控制器和动作路由器变量的键名和值，从URL解析的附加参数。它还允许访问作为公共成员的超全局变量，管理当前的基地址(Base URL)和请求URI。超全局变量不能在请求对象中赋值，但可以通过setParam/getParam方法设定/获取用户参数。

#### 超全局数据

通过`Zend_Controller_Request_Http`访问公共成员属性的超全局数据,有必要认识到一点，这些属性名(超全局数组的键)按照特定次序匹配超全局变量：1. GET，2.POST，3. COOKIE，4. SERVER，5. ENV。

特定的超全局变量也可以选择特定的方法来访问，如`$_POST['user']`可以调用请求对象的`getPost('user')`访问，`getQuery()`可以获取`$_GET`元素，`getHeader()`可以获取请求消息头。

#### GET和POST数据

需要注意：在请求对象中访问数据是没有经过任何过滤的，路由器和分发器根据任务来验证过滤数据，但在请求对象中没有任何处理。

#### 也获取原始 （Raw） POST 数据！

从 1.5.0 开始，也可以通过 `getRawBody()` 方法获取原始 post 数据。如果没有数据以那种方式提交，该方法返回 false，但 post 的全体（full boday）是个例外。

当开发一个 RESTful MVC 程序，这个对于接受内容相当有用。

可以在请求对象中使用`setParam()` 和`getParam()`来设置和获取用户参数。 路由器根据请求URI中的参数，利用这项功能请求对象设定参数。

#### getParam()不只可以获取用户参数

`getParam()`事实上从几个资源中获取参数。根据优先级排序：通过`setParam()`设置的用户参数，`GET` 参数，最后是`POST`参数。 通过该方法获取数据时需要注意这点。

如果你希望从你通过 `setParam()` 设置的参数中获取（参数），使用 `getUserParam()`。

另外，从 1.5.0 开始，可以锁定搜索哪个参数源，`setParamSources()` 允许指定一个空数组或者一个带有一个或多个指示哪个参数源被允许（缺省两者都被允许）的值 '_GET'或'_POST'的数组；如果想限制只访问 '_GET'，那么指定 `setParamSources(array('_GET'))` 。

#### Apache相关

如果使用apache的404处理器来传递请求到前端控制器，或者使用重写规则(rewrite rules)的PT标志，URI包含在`$_SERVER['REDIRECT_URL']`，而不是`$_SERVER['REQUEST_URI']`。如果使用这样的设定并获取无效的路由，应该使用`Zend_Controller_Request_Apache404`类代替默认的HTTP类：

    $request = new Zend_Controller_Request_Apache404();
    $front->setRequest($request);

这个类继承了`Zend_Controller_Request_Http`，并简单的修改了请求URI的自动发现(autodiscovery)，它可以用来作为简易替换器件(drop-in replacement)。

### 基地址和子目录

`Zend_Controller_Request_Http`允许在子目录中使用`Zend_Controller_Router_Rewrite`。`Zend_Controller_Request_Http`试图自动的检测你的基地址，并进行相应的设置。

例如，如果将 `index.php` 放在web服务器的名为`/projects/myapp/index.php`子目录中，基地址应该被设置为`/projects/myapp`。计算任何路由匹配之前将先从路径中去除这个字符串。这个字串需要被加入到任何路由前面。路由 `'user/:username'`将匹配类似`http://localhost/projects/myapp/user/martel` 和`http://example.com/user/martel`的URL。

#### URL检测区分大小写

基地址的自动检测是区分大小写的，因此需要确保URL与文件系统中的子目录匹配。否则将会引发异常。

如果基地址经检测不正确，可以利用`Zend_Controller_Request_Http`或者`Zend_Controller_Front`类的`setBaseUrl()`方法设置自己的基路径。`Zend_Controller_Front`设置最容易，它将导入基地址到请求对象。定制基地址的用法举例：

    /**
     * Dispatch Request with custom base URL with Zend_Controller_Front.
     */
    $router     = new Zend_Controller_Router_Rewrite();
    $controller = Zend_Controller_Front::getInstance();
    $controller->setControllerDirectory('./application/controllers')
               ->setRouter($router)
               ->setBaseUrl('/projects/myapp'); // set the base url!
    $response   = $controller->dispatch();

### 决定请求方式

`getMethod()` 允许你决定用于请求当前资源的 HTTP 请求方法。另外，当询问是否一个请求的特定类型是否已经存在，有许多方法允许你获得布尔响应：

- `isGet()`

- `isPost()`

- `isPut()`

- `isDelete()`

- `isHead()`

- `isOptions()`

这些基本用例是来创建 RESTful MVC 架构的。

### 删除 AJAX 请求

`Zend_Controller_Request_Http` 有一个初步的方法用来检测AJAX请求：`isXmlHttpRequest()`。这个方法寻找一个带有'XMLHttpRequest' 值的HTTP请求头`X-Requested-With`；如果发现，就返回true。

当前，这个头用下列JS库缺省地传递：

- Prototype/Scriptaculous (and libraries derived from Prototype)

- Yahoo! UI Library

- jQuery

- MochiKit

大多数 AJAX 库允许发送定制的HTTP请求头；如果你的库没有发送这个头，简单地把它作为一个请求头添加上确保`isXmlHttpRequest()` 方法工作。

## 子类化请求对象

所有请求对象的基请求类是抽象类`Zend_Controller_Request_Abstract`。定义了一些最基本的方法：

    abstract class Zend_Controller_Request_Abstract
    {
        /**
         * @return string
         */
        public function getControllerName();

        /**
         * @param string $value
         * @return self
         */
        public function setControllerName($value);

        /**
         * @return string
         */
        public function getActionName();

        /**
         * @param string $value
         * @return self
         */
        public function setActionName($value);

        /**
         * @return string
         */
        public function getControllerKey();

        /**
         * @param string $key
         * @return self
         */
        public function setControllerKey($key);

        /**
         * @return string
         */
        public function getActionKey();

        /**
         * @param string $key
         * @return self
         */
        public function setActionKey($key);

        /**
         * @param string $key
         * @return mixed
         */
        public function getParam($key);

        /**
         * @param string $key
         * @param mixed $value
         * @return self
         */
        public function setParam($key, $value);

        /**
         * @return array
         */
         public function getParams();

        /**
         * @param array $array
         * @return self
         */
        public function setParams(array $array);

        /**
         * @param boolean $flag
         * @return self
         */
        public function setDispatched($flag = true);

        /**
         * @return boolean
         */
        public function isDispatched();
    }

请求对象是请求环境的容器。控制器链实际上只需要知道如何设置和获取控制器、动作，可选的参数以及分发的状态。默认的，请求将使用controller和action键查询自己的参数来确定控制器和动作。

需要一个请求类来与特定的环境交互以获得需要的数据时，可以扩展该基类或它的衍生类。例如HTTP环境，CLI环境，或者PHP-GTK环境。