| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- <?xml version="1.0" encoding="UTF-8"?>
- <!-- Reviewed: no -->
- <sect1 id="zend.controller.exceptions">
- <title>Исключения</title>
- <sect2 id="zend.controller.exceptions.introduction">
- <title>Введение</title>
- <para>
- Компоненты MVC в Zend Framework используют фронт-контроллер, это
- означает, что все запросы к сайту проходят через единственную
- точку входа. Поэтому все исключения, брошенные внутри компонент, в
- конечном итоге доходят до фронт-контроллера, что позволяет
- разработчикам обрабатывать их в одном месте.
- </para>
- <para>
- Сообщения исключений и данные обратной трассировки часто содержат
- важную системную информацию, такую, как текст запросов SQL,
- местонахождение файлов и т.п. Поэтому в целях защиты вашего сайта
- <classname>Zend_Controller_Front</classname> по умолчанию отлавливает все
- исключения и регистрирует их в объектах ответа; в свою очередь,
- объект ответа по умолчанию не отображает сообщения исключений.
- </para>
- </sect2>
- <sect2 id="zend.controller.exceptions.handling">
- <title>Обработка исключений</title>
- <para>
- В компоненты MVC уже встроено несколько механизмов, дающих
- возможность обрабатывать исключения.
- </para>
- <itemizedlist>
- <listitem>
- <para>
- По умолчанию <link
- linkend="zend.controller.plugins.standard.errorhandler">плагин
- ErrorHandler</link> зарегистрирован и включен. Этот плагин
- предназначен для обработки:
- </para>
- <itemizedlist>
- <listitem><para>
- Ошибок, вызванных отсутствием запрошенного контроллера
- или действия
- </para></listitem>
- <listitem><para>
- Ошибок, произошедших внутри котроллеров действий
- </para></listitem>
- </itemizedlist>
- <para>
- Он действует как плагин <code>postDispatch()</code> и
- производит проверку, есть ли исключение диспетчера,
- контроллера действий и др. Если обнаружено
- исключение, то производится переход на контроллер-обработчик
- ошибок.
- </para>
- <para>
- Этот обработчик охватывает большинство исключительных
- ситуаций и позволяет элегантно обработать случаи
- отсутствующего контроллера или действия.
- </para>
- </listitem>
- <listitem>
- <para>
- <code>Zend_Controller_Front::throwExceptions()</code>
- </para>
- <para>
- Посредством передачи булевого значения <constant>TRUE</constant>
- этому методу вы говорите фронт-контроллеру, что будете
- самостоятельно обрабатывать исключения вместо того, чтобы
- они собирались объектом ответа или обрабатывались плагином
- ErrorHandler. Например:
- </para>
- <programlisting language="php"><![CDATA[
- $front->throwExceptions(true);
- try {
- $front->dispatch();
- } catch (Exception $e) {
- // самостоятельная обработка исключения
- }
- ]]></programlisting>
- <para>
- Возможно, этот метод является наиболее легким способом
- добавления собственной обработки исключений, охватывающей
- весь диапазон возможных исключений в вашем приложении.
- </para>
- </listitem>
- <listitem>
- <para>
- <code>Zend_Controller_Response_Abstract::renderExceptions()</code>
- </para>
- <para>
- Путем передачи булевого значения <constant>TRUE</constant>
- этому методу, вы говорите объекту ответа, что он должен
- вместе с заголовком и содержимым ответа выводить сообщения
- исключений и данные обратной трассировки. В этом случае
- будут отображены любые исключения, сгенерированные вашим
- приложением. Это рекомендуется только для непроизводственной
- среды.
- </para>
- </listitem>
- <listitem>
- <para>
- <code>Zend_Controller_Front::returnResponse()</code> и
- <code>Zend_Controller_Response_Abstract::isException()</code>.
- </para>
- <para>
- После передачи булевого значения <constant>TRUE</constant> методу
- <code>Zend_Controller_Front::returnResponse()</code> метод
- <code>Zend_Controller_Front::dispatch()</code> будет не
- выводить ответ, а возвращать его. Имея объект ответа, вы
- можете проверить, были ли отловлены исключения, используя
- его метод <code>isException()</code>, и извлекая их через
- метод <code>getException()</code>. Например:
- </para>
- <programlisting language="php"><![CDATA[
- $front->returnResponse(true);
- $response = $front->dispatch();
- if ($response->isException()) {
- $exceptions = $response->getException();
- // обработка исключений ...
- } else {
- $response->sendHeaders();
- $response->outputBody();
- }
- ]]></programlisting>
- <para>
- Основное преимущество этого метода по сравнению с
- <code>Zend_Controller_Front::throwExceptions()</code>
- состоит в том, что он позволяет управлять выводом ответа
- после обработки исключений. Это позволит отлавливать любые
- исключения в цепочке контроллеров, в отличие от плагина
- ErrorHandler.
- </para>
- </listitem>
- </itemizedlist>
- </sect2>
- <sect2 id="zend.controller.exceptions.internal">
- <title>Исключения в MVC, с которыми вы можете встретиться</title>
- <para>
- Различные компоненты MVC - объекты запросов, маршрутизаторов,
- диспетчеров, контроллеров действий, ответов могут бросать свои
- исключения. Одни исключения при определенных условиях могут быть
- замещены, другие используются для указания разработчику, что
- структура приложения, возможно, нуждается в рассмотрении.
- </para>
- <para>
- Некоторые примеры:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <code>Zend_Controller_Dispatcher::dispatch()</code> по
- умолчанию будет бросать исключение, если запрошен
- недействительный контроллер. Есть два способа решения этой
- проблемы.
- </para>
- <itemizedlist>
- <listitem>
- <para>
- Установить параметр
- <code>useDefaultControllerAlways</code>.
- </para>
- <para>
- Добавить в своем фронт-контроллере или диспетчере
- следующую директиву:
- </para>
- <programlisting language="php"><![CDATA[
- $front->setParam('useDefaultControllerAlways', true);
- // или
- $dispatcher->setParam('useDefaultControllerAlways', true);
- ]]></programlisting>
- <para>
- Когда этот флаг установлен, то диспетчер будет
- использовать контроллер и действие, приятые по
- умолчанию, вместо генерации исключения. Недостаток
- этого метода состоит в том, что на любые опечатки
- посетителя вашего сайта будет выводиться ваша
- главная страница, что может нанести вред в плане
- поисковой оптимизации сайта.
- </para>
- </listitem>
- <listitem>
- <para>
- Исключение, бросаемое методом
- <code>dispatch()</code>, является экземпляром класса
- <classname>Zend_Controller_Dispatcher_Exception</classname>,
- содержащим текст 'Invalid controller specified'
- (указан недействительный контроллер).
- Используйте один из методов, описанных в
- <link
- linkend="zend.controller.exceptions.handling">предыдущем разделе</link>,
- для отлова исключений и перенаправления на общую
- страницу ошибки или главную страницу.
- </para>
- </listitem>
- </itemizedlist>
- </listitem>
- <listitem>
- <para>
- <code>Zend_Controller_Action::__call()</code> будет
- бросать исключение, если в классе контроллера нет метода,
- соответствующего запрошенному действию. Весьма вероятно, что
- вы захотите, чтобы в этом случае в контроллере вызывалось
- действие по умолчанию. Ниже перечислены некоторые способы
- достичь этого:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- Создать подкласс <classname>Zend_Controller_Action</classname>
- и переопределить в нем метод <code>__call()</code>.
- Например:
- </para>
- <programlisting language="php"><![CDATA[
- class My_Controller_Action extends Zend_Controller_Action
- {
- public function __call($method, $args)
- {
- if ('Action' == substr($method, -6)) {
- $controller = $this->getRequest()->getControllerName();
- $url = '/' . $controller . '/index';
- return $this->_redirect($url);
- }
- throw new Exception('Invalid method');
- }
- }
- ]]></programlisting>
- <para>
- Пример выше перехватывает любые вызовы
- несуществующих методов действий и перенаправляет их
- на действие по умолчанию в контроллере.
- </para>
- </listitem>
- <listitem>
- <para>
- Создать подкласс
- <classname>Zend_Controller_Dispatcher</classname> и
- переопределить в нем метод <code>getAction()</code>
- так, чтобы он проводил проверку того, существует ли
- запрошенное действие. Например:
- </para>
- <programlisting language="php"><![CDATA[
- class My_Controller_Dispatcher extends Zend_Controller_Dispatcher
- {
- public function getAction($request)
- {
- $action = $request->getActionName();
- if (empty($action)) {
- $action = $this->getDefaultAction();
- $request->setActionName($action);
- $action = $this->formatActionName($action);
- } else {
- $controller = $this->getController();
- $action = $this->formatActionName($action);
- if (!method_exists($controller, $action)) {
- $action = $this->getDefaultAction();
- $request->setActionName($action);
- $action = $this->formatActionName($action);
- }
- }
- return $action;
- }
- }
- ]]></programlisting>
- <para>
- Код выше проверяет существование запрошенного
- действия в классе контроллера; если это действие не
- существует, то оно заменяется на действие по
- умолчанию.
- </para>
- <para>
- Этот способ хорош тем, что можно прозрачно заменить
- действие до финальной диспетчеризации. Тем не менее,
- он также подразумевает, что опечатки в URL могут
- обрабатываться как корректные, что не хорошо с точки
- зрения поисковой оптимизации.
- </para>
- </listitem>
- <listitem>
- <para>
- Использовать
- <code>Zend_Controller_Action::preDispatch()</code>
- или
- <code>Zend_Controller_Plugin_Abstract::preDispatch()</code>
- для определения ошибочно запрошенных действий.
- </para>
- <para>
- Создав подкласс <classname>Zend_Controller_Action</classname>
- и модифицировав метод <code>preDispatch()</code>, вы
- можете изменить все
- свои контроллеры так, чтобы они переходили
- к другому действию или производили
- HTTP-перенаправление до того, как будет выполнено
- текущее действие. Код для этого похож на код,
- приведенный выше, с переопределением
- <code>__call()</code>.
- </para>
- <para>
- Альтернативно, вы можете производить данную проверку
- в общем плагине. Преимущество этого подхода состоит
- в независимости контроллеров действий. Если ваше
- приложение содержит различные контроллеры действий и
- не все из них наследуют от одного и того же класса,
- то этот метод может обеспечить единообразие в
- обработке различных классов.
- </para>
- <para>
- Например:
- </para>
- <programlisting language="php"><![CDATA[
- class My_Controller_PreDispatchPlugin extends Zend_Controller_Plugin_Abstract
- {
- public function preDispatch(Zend_Controller_Request_Abstract $request)
- {
- $front = Zend_Controller_Front::getInstance();
- $dispatcher = $front->getDispatcher();
- $class = $dispatcher->getControllerClass($request);
- if (!$controller) {
- $class = $dispatcher->getDefaultControllerClass($request);
- }
- $r = new ReflectionClass($class);
- $action = $dispatcher->getActionMethod($request);
- if (!$r->hasMethod($action)) {
- $defaultAction = $dispatcher->getDefaultAction();
- $controllerName = $request->getControllerName();
- $response = $front->getResponse();
- $response->setRedirect('/' . $controllerName
- . '/' . $defaultAction);
- $response->sendHeaders();
- exit;
- }
- }
- }
- ]]></programlisting>
- <para>
- В этом примере мы проверяем, доступно ли в
- контроллере запрошенное действие. Если нет, то
- производится перенаправление на действие по
- умолчанию в этом контроллере и сразу же завершается
- выполнение скрипта.
- </para>
- </listitem>
- </itemizedlist>
- </listitem>
- </itemizedlist>
- </sect2>
- </sect1>
- <!--
- vim:se ts=4 sw=4 et:
- -->
|