CakePHP 1.2, Content-type, debug, RequestHandler и все-все-все

Как правильно, с точки зрения CakePHP, отвечать на запросы, если ответ требуется не html, а, например, json? Я считаю, что для получения ответа в нужном формате надо воспользоваться компонентом RequestHandler и указать в запросе расширение, в нашем случае ‘.json’. URL для запроса получится какой-то такой:

http://www.oursite.com/controller/action.json

Чтобы Cake не пугался этого расширения и вызывал правильное действие (action), в файл APP/config/routes.php добавим строку:

Router::parseExtensions('json');

Теперь надо разобраться с разными представлениями наших данных, чтобы по запросам к /action и /action.json возвращались разные виды. Для этого нужен компонент RequestHandler. Подключим его либо в самом контроллере, либо в родительском AppController:

var $components = array('RequestHandler');

Теперь шаблоны для view и layout Cake будет искать в поддиректориях /json. Т.е для layout надо будет создать файл /views/layout/json/default.ctp:

<?php echo $content_for_layout; ?>

и файл для действия /views/controller/json/action.ctp. Какой-то такой:

<?php echo $javascript->object($data); ?>

Это все и без меня все знают, ничего нового тут нет. Разве что cakebaker советует воспользоваться Zend::Json вместо стандартного помощника Javascript, дескать Zend’овская библиотека автоматом кодирует некоторые спецсимволы.

Но есть одна тонкость, недостаточно отраженная в документации — заголовок ответа Content-type. По всем ожиданиям он должен быть ‘application/json’, как это определено в коде компонента RequestHandler.

Однако, если включен режим отладки, т.е. Configure.debug > 0, то RequestHandler устанавливает Сontent-Type в text/html без вариантов. Это, в общем-то описано в документации и докопаться до этого можно.

Но совершенно неочевидно, что если режим отладки выключить в коде самого action, написав

Configure::write('debug', 0);

то это ничего уже не изменит! RequestHandler устанавливает заголовок во время загрузки компонента. Его метод startup() вызывает метод renderAs(), который вызывает метод respondAs(). Таким образом шаблоны подключаются нужные, те, которые из директорий /json, а Content-Type неправильный.

Лично я, пока, в acton, который должен отдавать данные в json, явно вызывываю RequestHandler->respondAs() повторно:

if (Configure::read()) {
    Configure::write('debug', 0);
    $this->RequestHandler->__responseTypeSet = null;
    $this->RequestHandler->renderAs('json');
}

Это заставляет RequestHandler изменить заголовок Content-type еще раз. Можно похожий код, с проверкой типа запроса и установкой режима отладки и заголовков попробовать запихнуть, скажем, в beforeRender. Обратите внимание на грязный хак с установкой свойства __responseTypeSet в NULL. Метод respondAs сохраняет в эту переменную тип ответа и повторно не будет срабатывать, если он уже был вызван.

Related Posts with Thumbnails
12.09.2009 • Метки: , • Рубрики: CakePHP
  • skiedr
    не проще ли в beforeFilter поместить код проверяющий раширение и выключающий дебаг?
    (c) core developer
blog comments powered by Disqus