# 5.4 请求上下文

我们实现了纯异步长驻的PHP Http Server,内存中的对象和数据均会复用,所以PHP的全局变量$_GET/$_POST/$_GLOBAL/$_REQUEST/类的静态属性要慎用,最好是不用。因为同一个Worker进程在同一时间有可能正在处理多个请求,比如A请求修改了全局的数据很有可能会影响B请求的正确处理。

那在处理请求过程中,如何能够让程序控制流向下传递数据,如何在同一个请求中共享和当前请求相关的数据呢,如何在任何地方方便的获取当前请求相关的数据?这就是请求上下文解决的问题。

## 请求上下文对象

在框架中封装了请求的上下文，在任何时候，有访问请求对象、响应输出对象、日志、日志ID、对象池操作、自定义请求数据的需要，都必须通过请求上下文对象来完成。

### 获取请求上下文对象

通常情况下，在MSF框架内直接使用$this->getContext()即可，包括在Controller，Model，Task。

需要特别注意的是，第三方或者自定义lib，通过对象池创建的对象是直接注入context属性，也是通过$this->getContext()的方法获取请求上下文对象。

### 请求体

提供了获取get/post/header/cookie/server等数据，通过请求上下文来获取，即$this->getContext()->getInput()，如下示例：

在控制器中使用示例：
```
// 指定获取 get 参数
$this->getContext()->getInput()->get('uid');
// 指定获取 post 参数
$this->getContext()->getInput()->post('uid');
// 指定获取 get & post 参数
$this->getContext()->getInput()->getPost('uid'); // 优先从 get 中取
$this->getContext()->getInput()->postGet('uid'); // 优先从 post 中去
// 获取所有 get & post 参数
$this->getContext()->getInput()->getAllPostGet();
// 获取 header 头信息
$this->getContext()->getInput()->getHeader('x-ngx-id');
// 获取所有 header 头信息
$this->getContext()->getInput()->getAllHeader();
// 获取所有 server 信息
$this->getContext()->getInput()->getAllServer();
// 获取原始的 post 包体
$this->getContext()->getInput()->getRawContent();
// 获取 cookie 参数
$this->getContext()->getInput()->getCookie('uid');
// 获取用户 ip
$this->getContext()->getInput()->getRemoteAddr();
// 获取 pathinfo 信息
$this->getContext()->getInput()->getPathInfo();
// 获取请求 uri
$this->getContext()->getInput()->getRequestUri();
```

### 响应体

提供了设置常用的header（如：content-type/cookie)等方法,响应json、html等数据格式

在控制器中调用：

```
// 响应数据
$this->getContext()->getOutput()->output($data, $status = 200);
```

响应结果如:

```
581af00d4b58d59d22e8d7a6
```

```
// 输出json数据格式
$this->getContext()->getOutput()->outputJson($data, $status = 200);
```

响应结果如:

```json
[
  "581af00d4b58d59d22e8d7a6",
  "581af00d4b58d59d22e8d7a7",
  "581af00d4b58d59d22e8d7a8",
]
```

`$status`为HTTP状态码，默认为200。

```
// 输出html数据（自动加载并渲染模板）
$this->getContext()->getOutput()->outputView($data);
```

但是在控制器基类中已经直接封装了三个同名的方法`Controller::output($data, $status = 200)`, `Controller::outputJson($data, $status = 200)`,`Controller::outputView($data, $view = null)`,可以直接使用。

```
// 设置Content-Type报头
$this->getContext()->getOutput()->setContentType('text/html; charset=UTF-8');
```

```
// 直接设置任何其他报头
$this->getContext()->getOutput()->setHeader('ContentType: text/html; charset=UTF-8');
```

另外,需要特别注意的是Http Server响应请求,实际上是调用了`$this->getContext()->getOutput()->end()`方法,所以如果仅仅是输出响应也可以直接调用:

```
// 直接响应http请求,不经过任何的加工
$this->getContext()->getOutput()->end('ok');
```

### 日志对象

日志对象提供框架的日志功能，是满足psr-3的标准日志对象，如下示例：

```
$this->getContext()->getLog()->error('致命错误')
$this->getContext()->getLog()->warning('警告错误')
$this->getContext()->getLog()->info('info日志')
$this->getContext()->getLog()->pushLog('key', $value)
```

### 对象池

对象池,任何有创建类对象的需求,均要使用对象池的方式,更多的介绍会在`对象池`,如下示例：

```
$this->getContext()->getObjectPool()->get(ClassName)
```

通常情况下，框架内置的类均`use MI;`，则可以直接使用`$this->getObject($className, ['构造参数列表'])`来创建对象，第三方lib也可使用trait来扩展类的功能。

### 自定义数据

用户自定义的请求上下文数据应用场景比如,多个地方均需要验证用户的登录信息,验证成功就可以将用户了uid/name/email等信息设置到请求的上下文,然后任何其他代码需要访问用户信息就可以直接使用。另外需要注意的是尽可能设置为标题和数组的自定义数据,减少内存泄露的风险。

```
// 设置自定义key对应的value
$this->getContext()->setUserDefined('key', 'value')
// 获取自定义的key对应的value
$this->getContext()->getUserDefined('key')
```

### 其他

```
// 获取当前请求的控制器名
$this->getContext()->getControllerName()
// 获取当前请求的控制器方法名
$this->getContext()->getActionName()
```

# links
  * [目录](../README.md)
  * 上一节: [异步HttpClient](5.3-异步HttpClient.md)
  * 下一节: [连接池](5.5-连接池.md)
