5.4 请求上下文
最后更新于:2022-03-31 23:47:44
# 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');
```
### 日志对象
日志对象提供框架的日志功能,是满足pr-4的标准日志对象,如下示例:
```
$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()
```