如何使用 CakePHP 3.4 输出自定义 HTTP 正文内容?回显导致“无法发出标头”错误
Posted
技术标签:
【中文标题】如何使用 CakePHP 3.4 输出自定义 HTTP 正文内容?回显导致“无法发出标头”错误【英文标题】:How to output custom HTTP body contents with CakePHP 3.4? Echoing causes "Unable to emit headers" error 【发布时间】:2017-07-11 18:04:12 【问题描述】:使用 Cakephp 3.4、PHP 7.0。
我正在尝试做一个非常简单的控制器方法来输出一些 JSON。它正在输出“无法修改标题...”。
public function test()
$this->autoRender = false;
echo json_encode(['method' => __METHOD__, 'class' => get_called_class()]);
浏览器输出
"method":"App\\Controller\\SomeController::test", "class":"App\\Controller\\SomeController"
Warning (512): Unable to emit headers. Headers sent in file=...
Warning (2): Cannot modify header information - headers already sent by (output started at ...)
Warning (2): Cannot modify header information - headers already sent by (output started at ...)
我完全理解为什么 PHP 会抱怨这一点。问题是为什么 CakePHP 会抱怨,我该怎么办?
需要注意的是,CakePHP 2.x 允许这样做。
【问题讨论】:
【参考方案1】:CakePHP 3 有一个名为 JSON views 的东西,它允许您返回 JSON 数据。我之前没有做过任何 CakePHP,所以我不知道请求的生命周期,但值得研究一下。
【讨论】:
【参考方案2】:控制器不应该回显数据!回显数据会导致各种问题,从测试环境无法识别数据,到无法发送标头,甚至数据被截断。
这样做在 CakePHP 2.x 中已经是错误的了,尽管它可能在某些甚至大多数情况下都有效。随着新 HTTP 堆栈的引入,CakePHP 现在在回显响应之前显式检查发送的标头,并相应地触发错误。
发送自定义输出的正确方法是配置并返回响应对象,或者使用序列化视图,在 3.x 中仍然如此。
引用自文档:
控制器动作通常使用
Controller::set()
来创建视图用来渲染视图层的上下文。由于 CakePHP 使用的约定,您不需要手动创建和呈现视图。相反,一旦控制器动作完成,CakePHP 将处理渲染和交付视图。如果出于某种原因您想跳过默认行为,您可以从带有完整创建响应的操作中返回
Cake\Network\Response
对象。
* 从 3.4 开始,这将是 \Cake\Http\Response
Cookbook > Controllers > Controller Actions
配置响应
使用符合 PSR-7 的接口
$content = json_encode(['method' => __METHOD__, 'class' => get_called_class()]);
$this->response = $this->response->withStringBody($content);
$this->response = $this->response->withType('json');
// ...
return $this->response;
符合 PSR-7 的接口使用不可变方法,因此使用了 withStringBody()
和 withType()
的返回值。在 CakePHP withStringBody() 不可用,可以直接写入 body 流,不会改变响应对象的状态:
$this->response->getBody()->write($content);
使用已弃用的接口
$content = json_encode(['method' => __METHOD__, 'class' => get_called_class()]);
$this->response->body($content);
$this->response->type('json');
// ...
return $this->response;
使用序列化视图
$content = ['method' => __METHOD__, 'class' => get_called_class()];
$this->set('content', $content);
$this->set('_serialize', 'content');
这还需要使用请求处理程序组件,并启用扩展解析和使用附加了 .json
的相应 URL,或者发送带有 application/json
接受标头的正确请求。
另见
Cookbook > Controllers > Controller Actions Cookbook > Request & Response Objects > Setting the Body Cookbook > Views > JSON and XML views PHP FIG Standards > PSR-7 HTTP message interfaces【讨论】:
以上是关于如何使用 CakePHP 3.4 输出自定义 HTTP 正文内容?回显导致“无法发出标头”错误的主要内容,如果未能解决你的问题,请参考以下文章
Cakephp MiddleWare 类中如何导入自定义组件?
CakePHP 3自定义验证:如何在编辑期间将新值与旧值进行比较?