Контент


[Ko3.3] Изменения в обработке запросов

В ветке 3.3 продолжилось дальнейшее развитие классов, участвующих в выполнении запросов (Request, Response, Controller, HTTP и т.д.). Так как поменялись некоторые важные нюансы, решил выделить изменения в отдельную статью.

Выполнение экшена контроллера

#4377. В версии 3.2 запуском экшенов управлял объект Request (а точнее, метод execute_request() класса Request_Client_Internal). Он запускал before(), затем проверял существование нужного экшена и выполнял его, и под конец запускал метод after(). Концепция удобная, но расположение внутри класса Request_Client_Internal затрудняло внесение изменений в данный процесс. Даже если подкорректировать алгоритм через класс-пустышку, то это затронет абсолютно все внутренние запросы проекта.

В версии 3.3 предложен более удобный способ. Базовый класс Controller имеет метод execute(), внутри которого и расположился описанный выше алгоритм. В результате мы можем легко поменять его под свои нужды, в том числе для отдельной группы контроллеров. Очень удобно. Также можно ловить исключения для целой группы экшенов в одном месте.

Метод execute() должен возвращать объект Response!

HMVC

Переработана идеология работы подзапросов. Теперь они изолированы от родительского запроса. В частности, брошенное исключение или редирект не повлияют на родительский запрос. В любом случае вернется объект класса Response.

В версии 3.2 после брошенного исключения возвращался текст ответа (т.е. строка), а редирект в дочернем подзапросе приводил к остановке выполнения запросов и перенаправлению на новый адрес. Теперь же можно спокойно анализировать результат работы запроса, в частности его статус (с помощью метода status()):

// пример обработки HMVC в 3.3
$response = Request::factory('foo/bar')->execute();
switch ($response->status()) {
    case 404: 
         // обрабатываем 404
         break;
    ...
    case 200:
        // все ок, можно показывать результат
        break;
}

Коды HTTP-ответов

Обработка HTTP-статусов теперь облегчена по сравнению с 3.2, не надо устанавливать и потом анализировать $this->response->status(), достаточно просто выбросить HTTP_Exception:

// отправляем погуглить ;)
throw HTTP_Exception::factory(302, 'http://google.com');

Обработчик исключений сгенерирует Response с соответствующим статусом, заполнит его тело (body) и заголовки (headers) если надо.

HTTP_Exception

Изменения в обработке исключений связаны с изменениями в HMVC. Теперь каждое исключение должно отдавать объект Response в качестве результата работы. Причем механизм различен для семейства исключений HTTP и всех остальных.

Исключения класса HTTP_Exception

В Kohana существует целый ряд исключений для различных HTTP-кодов. Но если в Kohana 3.2 они принципиально ничем не отличались от остальных исключений (разве что для них были заранее заготовлены тексты сообщений в свойстве Response::$messages), то в 3.3 для них можно легко реализовать красивые страницы сообщений. Все, что нужно сделать — написать свой метод get_response() в соответствующем классе.

Например, вот так осуществляется вывод по умолчанию (метод в классе HTTP_Exception):

public function get_response()
{
	return Kohana_Exception::response($this);
}

Это вызов стандартного обработчика, который всегда используется со всеми остальными исключениями (см. ниже). Никто не мешает нам вывести специальную страницу для кода 404 (страница не найдена):

// в классе HTTP_Exception_404
public function get_response()
{
	Kohana_Exception::log($this);
	return Response::factory()
		->status(404)
		->body(View::factory('http/404'));
}

Обработчик очень простой — сообщение об ошибке логируется (стандартный для исключений метод Kohana_Exception::log()), создается объект Response, в который передается статус 404 и специальная вьюшка для отображения страницы Not Found.

Обработчик по умолчанию

Для всех исключений, кроме HTTP_Exception с собственным методом get_response(), будет применен метод Kohana_Exception::response($exception). В нем проводится сбор данных об исключении (код, текст ошибки, трассировка и т.д.) и передается в шаблон для генерации стандартной страницы ошибки (она ничем не отличается от предыдущих версий). Имя используемого шаблона конфигурируется, для этого надо изменить свойство Kohana_Exception::$error_view (по умолчанию ‘kohana/error‘).

Редирект

Выше я уже отметил, что редирект в версии 3.3 не ведет к немедленному прекращению выполнения запроса. Он теперь всего лишь выбрасывает HTTP_Exception с переданным кодом и URI назначения. А тот, в свою очередь, должен вернуть объект Response со статусом и заголовком Location. Если запрос является основным, то объект Response отошлет заголовки, и редирект все же произойдет. А вот HMVC-запросы ломать работу основного не будут, и вместо неожиданных редиректов скромно вернут ответ со статусом 30x.

Помимо общих принципов, изменилось и собственно API для осуществления редиректов. Метод redirect($url = '', $code = 302) переместился из класса Request в классы Controller и HTTP, причем статический (!) метод в контроллере (Controller::redirect()) является оберткой вокруг метода HTTP::redirect().

HTTP-кеширование

В версии 3.2 обработка заголовков HTTP-кеширования велась с помощью метода check_cache($etag = NULL, Request $request = NULL) класса Response. В ветке 3.3 данный метод переместился в класс Controller, и нужды в передаче параметра $request уже нет (передается $this->request).

Для тех, кто не знает, зачем это нужно, предлагаю посмотреть кусок кода из Userguide, метод отвечает за отображение медиафайлов (css, js и т.д.):

if ($file = Kohana::find_file('media/guide', $file, $ext))
{
	// Check if the browser sent an "if-none-match: <etag>" header, and tell if the file hasn't changed
	$this->check_cache(sha1($this->request->uri()).filemtime($file));
 
	// Send the file content as the response
	$this->response->body(file_get_contents($file));
 
	// Set the proper headers to allow caching
	$this->response->headers('content-type',  File::mime_by_ext($ext));
	$this->response->headers('last-modified', date('r', filemtime($file)));
}

Etag вычисляется на основе переданного URI и даты последнего изменения запрашиваемого файла. Если браузер передал заголовок if-none-match с совпавшим идентификатором etag, то до file_get_contents() выполнение не дойдет. От сервера вернется ответ с кодом 304 Not Modified.

Заключение

В данной статье я описал изменения, влияющие на основную задачу ядра — обработку запросов (request flow). Отдельные изменения, затронувшие менее важные задачи (в основном это изменения хэлперов и библиотек) будут описаны в следующей статье.

Google Bookmarks Digg Reddit del.icio.us Ma.gnolia Technorati Slashdot Yahoo My Web News2.ru БобрДобр.ru RUmarkz Ваау! Memori.ru rucity.com МоёМесто.ru Mister Wong

Опубликовано в Kohana3.

Теги: , , , .


Комментарии (12)

Будьте в курсе обсуждения, подпишитесь на RSS ленту комментариев к этой записи.

  1. aktuba пишет:

    А как скачать 3.3? Тут скачивается без core, а в core нет тега 3.3 (

  2. aktuba пишет:

    Да, уже скачал, после того, как написал комментарий )

  3. biakaveron пишет:

    Проще через Git )))

  4. Федот пишет:

    Спасибо большое за статью.
    Как по мне изменения очень правильные, и нужные. В верном направлении двигаются товарищи =)

  5. SVat пишет:

    Изменился порядок аргументов – теперь сперва $code, затем $uri.

    Вроде не изменился: https://github.com/kohana/core/blob/3.3/develop/classes/Kohana/Controller.php#L125

  6. SVat пишет:

    И зачем было делать для редиректа в контроллере именно статический метод?!

  7. biakaveron пишет:

    @SVat
    Да, меня странно переклинило :) Порядок менялся, но в другую сторону, и в итоге он остался прежним. Спасибо, поправил статью.

    По поводу его «статичности», вот тут интересное обсуждение. Есть подозрение, что этот метод еще поменяет свое содержание, или вообще будет удален из класса Controller.

  8. Spider пишет:

    Иван, подскажи, пожалуйста, как в 3.3 получить объект текущего Response извне контроллера? В 3.2 это выглядело как Request::current()->response()

  9. biakaveron пишет:

    В 3.3 объект Response не хранится внутри Request. Он генерируется во время execute(), и отдается как результат его работы (https://github.com/kohana/core/blob/3.3/develop/classes/Kohana/Request/Client.php#L144, https://github.com/kohana/core/blob/3.3/develop/classes/Kohana/Request.php#L989). Т.е. на результат работы запроса извне повлиять нельзя (ИМХО, это вполне логично).

    Как-то так.

  10. Spider пишет:

    Жаль. Просто я написал хелпер, чтобы вместо $this->response->body($view->render()) писать просто render($view);

Продолжение обсуждения

  1. [Ko3.3] Прочие изменения ядра | Изучаем Web ссылается на эту запись on 26 апреля 2012

    [...] предыдущей статье я описал основные изменения ядра фреймворка, [...]



Можно включить подсветку кода: <code><pre lang="">...</pre></code>
Разрешены некоторые HTML теги

или используйте trackback.