Использование 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 позволил это.
Контроллеры никогда не должны отображать данные! Повторное отображение данных может привести к всевозможным проблемам: от данных, не распознаваемых в тестовой среде, до невозможности отправки заголовков и даже к удалению данных.
Это было неправильно в CakePHP 2.x, хотя в некоторых, возможно, даже в большинстве случаев это работало. С введением нового стека HTTP CakePHP теперь явно проверяет отправленные заголовки перед повторением ответа и соответственно вызывает ошибку.
Правильный способ отправки настраиваемого вывода состоял в том, чтобы сконфигурировать и вернуть объект ответа или использовать сериализованные представления, и в 3.x он все тот же.
Цитата из документов:
Действия контроллера обычно используют
Controller::set()
создать контекст, который View использует для визуализации слоя представления. Из-за соглашений, которые использует CakePHP, вам не нужно создавать и визуализировать представление вручную. Вместо этого после завершения действия контроллера CakePHP будет обрабатывать рендеринг и доставку представления.Если по какой-либо причине вы хотите пропустить поведение по умолчанию, вы можете вернуть
Cake\Network\Response
объект от действия с полностью созданным ответом.
* Начиная с 3.4 это будет \Cake\Http\Response
Кулинарная книга> Контроллеры> Действия контроллера
$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
принять заголовок.
CakePHP 3 имеет то, что называется JSON просмотров которые позволяют вам возвращать данные JSON. Я не делал CakePHP раньше, так что я не знаю жизненный цикл запроса, но это стоит посмотреть.