Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
275 views
in Technique[技术] by (71.8m points)

php - How to output custom HTTP body contents with CakePHP 3.4? Echoing causes "Unable to emit headers" error

Using CakePHP 3.4, PHP 7.0.

I'm attempting to do a really simple controller method to output some JSON. It is outputting "Cannot modify headers...".

public function test() {
    $this->autoRender = false;
    echo json_encode(['method' => __METHOD__, 'class' => get_called_class()]);
}

Browser output

{"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 ...)

I fully understand why PHP complains about this. The question is why does CakePHP complain and what can I do about it?

It should be noted that CakePHP 2.x allowed this.

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Controllers should never echo data! Echoing data can lead to all kinds of problems, from the data not being recognized in the test environment, to headers not being able to be sent, and even data being cut off.

Doing it that way was already wrong in CakePHP 2.x, even though it might have worked in some, maybe even most situations. With the introduction of the new HTTP stack, CakePHP now explicitly checks for sent headers before echoing the response, and will trigger an error accordingly.

The proper way to send custom output was to configure and return the response object, or to use serialized views, and it's still the same in 3.x.

Quote from the docs:

Controller actions generally use Controller::set() to create a context that View uses to render the view layer. Because of the conventions that CakePHP uses, you don’t need to create and render the view manually. Instead, once a controller action has completed, CakePHP will handle rendering and delivering the View.

If for some reason you’d like to skip the default behavior, you can return a CakeNetworkResponse object from the action with the fully created response.

* As of 3.4 that would be CakeHttpResponse

Cookbook > Controllers > Controller Actions

Configure the response

Using the PSR-7 compliant interface

$content = json_encode(['method' => __METHOD__, 'class' => get_called_class()]);

$this->response = $this->response->withStringBody($content);
$this->response = $this->response->withType('json');
// ...

return $this->response;

The PSR-7 compliant interface uses immutable methods, hence the utilization of the return value of withStringBody() and withType(). In CakePHP < 3.4.3, withStringBody() is not available, and you can instead directly write to the body stream, which will not change the state of the response object:

$this->response->getBody()->write($content);

Using the deprecated interface

$content = json_encode(['method' => __METHOD__, 'class' => get_called_class()]);

$this->response->body($content);
$this->response->type('json');
// ...

return $this->response;

Use a serialized view

$content = ['method' => __METHOD__, 'class' => get_called_class()];

$this->set('content', $content);
$this->set('_serialize', 'content');

This requires to also use the request handler component, and to enable extensing parsing and using correponsing URLs with .json appended, or to send a proper request with a application/json accept header.

See also


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...