# 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()
```
- 0 文檔說明
- 1 為什么研發新框架
- 1.1 傳統php-fpm工作模式的問題
- 1.2 壓測數據對比
- 1.3 小結
- 2 微服務框架研發概覽
- 2.1 通信框架技術選型
- 2.2 swoole
- 2.3 協程原理
- 2.4 異步、并發
- 2.5 小結
- 3 框架運行環境
- 3.1 環境變量
- 3.2 運行代碼
- 3.3 docker
- 3.4 小結
- 4 框架結構
- 4.1 結構概述
- 4.2 控制器
- 4.3 模型
- 4.4 視圖
- 4.5 同步任務
- 4.6 配置
- 4.7 路由
- 4.8 小結
- 5 框架組件
- 5.1 協程
- 5.2 類的加載
- 5.3 異步Http Client
- 5.4 請求上下文
- 5.5 連接池
- 5.6 對象池
- 5.7 RPC
- 5.8 公共庫
- 5.9 RESTful
- 5.10 多語言
- 5.11 雜項
- 5.12 小結
- 6 常見問題
- 7 附錄