Как вывести собственное содержимое тела HTTP с CakePHP 3.4? Повторяющиеся причины & quot; Невозможно испустить заголовки & quot; ошибка

Использование 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 позволил это.

3

Решение

Контроллеры никогда не должны отображать данные! Повторное отображение данных может привести к всевозможным проблемам: от данных, не распознаваемых в тестовой среде, до невозможности отправки заголовков и даже к удалению данных.

Это было неправильно в CakePHP 2.x, хотя в некоторых, возможно, даже в большинстве случаев это работало. С введением нового стека HTTP CakePHP теперь явно проверяет отправленные заголовки перед повторением ответа и соответственно вызывает ошибку.

Правильный способ отправки настраиваемого вывода состоял в том, чтобы сконфигурировать и вернуть объект ответа или использовать сериализованные представления, и в 3.x он все тот же.

Цитата из документов:

Действия контроллера обычно используют Controller::set() создать контекст, который View использует для визуализации слоя представления. Из-за соглашений, которые использует CakePHP, вам не нужно создавать и визуализировать представление вручную. Вместо этого после завершения действия контроллера CakePHP будет обрабатывать рендеринг и доставку представления.

Если по какой-либо причине вы хотите пропустить поведение по умолчанию, вы можете вернуть Cake\Network\Response объект от действия с полностью созданным ответом.

* Начиная с 3.4 это будет \Cake\Http\Response

Кулинарная книга> Контроллеры> Действия контроллера

Настройте ответ

Использование 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 < 3.4.3, withStringBody() недоступен, и вы можете вместо этого напрямую записать в поток тела, что не изменит состояние объекта ответа:

$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');

Для этого также необходимо использовать компонент обработчика запросов и включить расширенный анализ и использование соответствующих URL-адресов с .json или приложить соответствующий запрос с application/json принять заголовок.

Смотрите также

5

Другие решения

CakePHP 3 имеет то, что называется JSON просмотров которые позволяют вам возвращать данные JSON. Я не делал CakePHP раньше, так что я не знаю жизненный цикл запроса, но это стоит посмотреть.

0