thinkphp控制器需用file_get_contents('php://input')读取json等非表单post数据,因$request->post()仅支持application/x-www-form-urlencoded和multipart/form-data;swoole环境推荐$request->getcontent()。

ThinkPHP控制器接收原始POST数据,不能只依赖 $this->request->post() 或 input('post.') —— 这些方法只解析 Content-Type: application/x-www-form-urlencoded 和 multipart/form-data 类型的数据,对 JSON、XML、纯文本或自定义二进制 payload 会直接返回空数组。
为什么 $this->request->post() 拿不到 JSON 数据
ThinkPHP 的 Request::post() 内部调用的是 PHP 的 $_POST 超全局变量,而 $_POST 只在请求头为 application/x-www-form-urlencoded 或 multipart/form-data 时由 PHP 自动解析填充。当前端发来 Content-Type: application/json 时,PHP 不解析,$_POST 为空,框架自然也读不到。
- 现象:前端用
fetch({ method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({a:1}) }),后端$this->request->post()返回[] - 验证方式:打印
$this->request->header('content-type'),确认是否为application/json或其他非表单类型 - 正确做法:跳过框架解析层,直接读取原始输入流
用 file_get_contents('php://input') 读原始数据
这是最通用、兼容性最好的方式,适用于 JSON、XML、YAML、Protobuf 等任意非表单格式的 POST 数据(但不支持 multipart/form-data)。
-
php://input是只读流,只能读一次;若你已在框架中触发过$this->request->param()或$this->request->post(),可能已被消耗,需在控制器方法开头立即读取 - 示例代码:
$raw = file_get_contents('php://input'); if (empty($raw)) { throw new \think\Exception('Empty raw POST data'); } $data = json_decode($raw, true); if (json_last_error() !== JSON_ERROR_NONE) { throw new \think\Exception('Invalid JSON format'); } - 注意:
php://input在enctype="multipart/form-data"场景下不可用,此时应改用$_FILES+$_POST组合处理
不同 Content-Type 下该选哪种方式
别硬套一种写法,必须根据请求头动态适配:
-
application/x-www-form-urlencoded→ 用$this->request->post('key')或input('post.key'),安全且带自动过滤 -
application/json→ 必须用file_get_contents('php://input')+json_decode(),别试图用$this->request->param()强转 -
text/xml或application/soap+xml→ 同样走php://input,再用simplexml_load_string()解析 -
application/octet-stream(如上传二进制文件)→php://input直接保存,或配合stream_copy_to_stream()流式写入 - 混合场景(比如同时传 JSON body + query 参数)→
$this->request->get()拿 query,php://input拿 body,不要混用
容易被忽略的坑:Swoole / Swoft / RoadRunner 环境下 php://input 失效
在常驻内存的协程服务器(如 Swoole)中,php://input 可能被提前读取或不可用,尤其在启用 enable_static_handler 或中间件已消费流时。
- 解决方案:优先使用
$this->request->getContent()(ThinkPHP 6+ 支持),它封装了对不同 SAPI 的兼容读取逻辑 - TP6 示例:
$raw = $this->request->getContent();,比手写php://input更可靠 - 若用 TP5.1,可加一层 fallback:
file_get_contents('php://input') ?: ($GLOBALS['HTTP_RAW_POST_DATA'] ?? ''),但后者需 php.ini 开启always_populate_raw_post_data(已废弃,不推荐) - 调试建议:在控制器开头加
var_dump($GLOBALS['HTTP_RAW_POST_DATA'], file_get_contents('php://input'), $this->request->getContent());对比三者输出
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











