Zend_Controller-Exceptions.xml 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- EN-Revision: 17593 -->
  3. <!-- Reviewed: no -->
  4. <sect1 id="zend.controller.exceptions">
  5. <title>Excepciones MVC</title>
  6. <sect2 id="zend.controller.exceptions.introduction">
  7. <title>Introducción</title>
  8. <para> Los componentes <acronym>MVC</acronym> en Zend Framework utilizan
  9. un Front Controller, lo que significa que todas las solicitudes de
  10. un determinado sitio pasarán por un solo punto de entrada. Como
  11. resultado, todas las excepciones burbujearán eventualmente hacia
  12. arriba hasta el Front Controller, permitiendo al desarrollador
  13. manejarlos en un solo lugar. </para>
  14. <para> Sin embargo, los mensajes de excepción y la información de
  15. backtrace contienen a menudo información sensible del sistema, como
  16. declaraciones <acronym>SQL</acronym>, ubicaciones de archivos y
  17. otras cosas más. Para ayudar a proteger su sitio, por defecto
  18. <classname>Zend_Controller_Front</classname> captura todas las
  19. excepciones y las registra con el objeto respuesta; a su vez, y por
  20. defecto, el objeto respuesta no muestra mensajes de excepción.
  21. </para>
  22. </sect2>
  23. <sect2 id="zend.controller.exceptions.handling">
  24. <title>Manejando las Excepciones</title>
  25. <para> Ya hay varios mecanismos construidos en los componentes de
  26. <acronym>MVC</acronym>, que le permiten manejar excepciones. </para>
  27. <itemizedlist>
  28. <listitem>
  29. <para> Por defecto, el <link
  30. linkend="zend.controller.plugins.standard.errorhandler"
  31. >error handler plugin</link> está registrado y activo.
  32. Este plugin fue diseñado para manejar: </para>
  33. <itemizedlist>
  34. <listitem>
  35. <para>Errores debido a controladores o acciones
  36. perdidas</para>
  37. </listitem>
  38. <listitem>
  39. <para>Errores ocurriendo dentro de controladores de
  40. acción</para>
  41. </listitem>
  42. </itemizedlist>
  43. <para> Operan como un plugin de
  44. <methodname>postDispatch()</methodname>, y comprueban
  45. para ver si un despachador, controlador de acción, o de otra
  46. excepción ha ocurrido. Si así fuera, lo remite a un
  47. controlador de manejo de errores. </para>
  48. <para> Este manejador abarcará la mayoría de las situaciones
  49. excepcionales, y maneja airosamente controladores y acciones
  50. perdidos. </para>
  51. </listitem>
  52. <listitem>
  53. <para>
  54. <methodname>Zend_Controller_Front::throwExceptions()</methodname>
  55. </para>
  56. <para> Pasando a este método un valor booleano
  57. <constant>TRUE</constant>, puede decirle al front
  58. controller que, en lugar de sumar excepciones en el objeto
  59. respuesta o utilizando el plugin de manejo de errores,
  60. prefiere manejarlos usted mismo. Como ejemplo: </para>
  61. <programlisting language="php"><![CDATA[
  62. $front->throwExceptions(true);
  63. try {
  64. $front->dispatch();
  65. } catch (Exception $e) {
  66. // usted mismo maneja las excepciones
  67. }
  68. ]]></programlisting>
  69. <para> Este método es probablemente la forma más fácil de añadir
  70. un manejo de excepciones personalizado que abarque toda la
  71. gama de posibles excepciones a su aplicación de front
  72. controller. </para>
  73. </listitem>
  74. <listitem>
  75. <para>
  76. <methodname>Zend_Controller_Response_Abstract::renderExceptions()</methodname>
  77. </para>
  78. <para> Al pasar a este método un valor booleano
  79. <constant>TRUE</constant>, le esta diciendo al objeto
  80. respuesta que debe emitir un mensaje de excepción y
  81. backtrace cuando se renderiza a sí mismo. En este escenario,
  82. se mostrará cualquier excepción planteada por su aplicación.
  83. Esto no es recomendable para entornos de producción, pero sí
  84. en desarrollo. </para>
  85. </listitem>
  86. <listitem>
  87. <para>
  88. <methodname>Zend_Controller_Front::returnResponse()</methodname>
  89. y
  90. <methodname>Zend_Controller_Response_Abstract::isException()</methodname>. </para>
  91. <para> Pasando un valor booleano <constant>TRUE</constant> a
  92. <methodname>Zend_Controller_Front::returnResponse()</methodname>,
  93. <methodname>Zend_Controller_Front::dispatch()</methodname>
  94. no renderizará la respuesta, sino que la devolverá. Una vez
  95. que tiene la respuesta, entonces puede probar ver si todas
  96. las excepciones fueron atrapadas usando su método
  97. <methodname>isException()</methodname>, y recuperando
  98. las excepciones a través del método
  99. <methodname>getException()</methodname>. Como ejemplo: </para>
  100. <programlisting language="php"><![CDATA[
  101. $front->returnResponse(true);
  102. $response = $front->dispatch();
  103. if ($response->isException()) {
  104. $exceptions = $response->getException();
  105. // maneje las excepciones ...
  106. } else {
  107. $response->sendHeaders();
  108. $response->outputBody();
  109. }
  110. ]]></programlisting>
  111. <para> La principal ventaja que este método ofrece por sobre
  112. <methodname>Zend_Controller_Front::throwExceptions()</methodname>
  113. es que le permite renderizar condicionalmente la respuesta
  114. después de manejar la excepción. Esta capturará cualquier
  115. excepción en la cadena de controladores, a diferencia del
  116. plugin de manejo de errores. </para>
  117. </listitem>
  118. </itemizedlist>
  119. </sect2>
  120. <sect2 id="zend.controller.exceptions.internal">
  121. <title>Excepciones MVC que Usted Pueda Encontrar</title>
  122. <para> Los diversos componentes de <acronym>MVC</acronym> -- solicitud,
  123. router, despachador, controlador de acción, y los objetos respuesta
  124. -- pueden arrojar excepciones en ocasiones. Algunas excepciones
  125. puede ser condicionalmente anuladas, y otras se usan para indicar al
  126. desarrollador que puede necesitar re-considerar la estructura de su
  127. aplicación. </para>
  128. <para>Como algunos ejemplos:</para>
  129. <itemizedlist>
  130. <listitem>
  131. <para>
  132. <methodname>Zend_Controller_Dispatcher::dispatch()</methodname>
  133. hará, por defecto, arrojar una excepción si se hace un
  134. requerimiento a un controlador no válido. Hay dos maneras
  135. recomendadas para lidiar con esto. </para>
  136. <itemizedlist>
  137. <listitem>
  138. <para>Establecer el parámetro
  139. <methodname>useDefaultControllerAlways</methodname>. </para>
  140. <para> En su front controller, o en su despachador,
  141. añada la siguiente directiva: </para>
  142. <programlisting language="php"><![CDATA[
  143. $front->setParam('useDefaultControllerAlways', true);
  144. // o
  145. $dispatcher->setParam('useDefaultControllerAlways', true);
  146. ]]></programlisting>
  147. <para> Cuando este flag está establecido, el despachador
  148. utilizará el controlador y la acción por defecto en
  149. lugar de lanzar una excepción. La desventaja de este
  150. método es que cualquier error ortográfico que un
  151. usuario haga cuando acceda a su sitio lo resolverá y
  152. mostrará su página de inicio, y que puede causar
  153. estragos con la optimización para los motores de
  154. búsqueda. </para>
  155. </listitem>
  156. <listitem>
  157. <para> La excepción arrojada por
  158. <methodname>dispatch()</methodname> es una
  159. <classname>Zend_Controller_Dispatcher_Exception</classname>
  160. conteniendo el texto 'Invalid controller specified'.
  161. Use uno de los métodos descriptos de <link
  162. linkend="zend.controller.exceptions.handling">la
  163. sección anterior</link> para atrapar la
  164. excepción, y luego redireccionar a una página
  165. genérica de error o a la página de inicio. </para>
  166. </listitem>
  167. </itemizedlist>
  168. </listitem>
  169. <listitem>
  170. <para>
  171. <methodname>Zend_Controller_Action::__call()</methodname>
  172. arrojará una
  173. <classname>Zend_Controller_Action_Exception</classname>
  174. si no puede despachar una acción inexistente a un método. Es
  175. probable que desee utilizar alguna acción por defecto en el
  176. controlador en casos como este. Formas de lograr esto
  177. incluyen: </para>
  178. <itemizedlist>
  179. <listitem>
  180. <para> Subclasear
  181. <classname>Zend_Controller_Action</classname> y
  182. anular el método <methodname>__call()</methodname>.
  183. Como ejemplo: </para>
  184. <programlisting language="php"><![CDATA[
  185. class My_Controller_Action extends Zend_Controller_Action
  186. {
  187. public function __call($method, $args)
  188. {
  189. if ('Action' == substr($method, -6)) {
  190. $controller = $this->getRequest()->getControllerName();
  191. $url = '/' . $controller . '/index';
  192. return $this->_redirect($url);
  193. }
  194. throw new Exception('Invalid method');
  195. }
  196. }
  197. ]]></programlisting>
  198. <para> El ejemplo anterior intercepta cualquier llamada
  199. a un método de acción indefinido y redirecciona a la
  200. acción predeterminada en el controlador. </para>
  201. </listitem>
  202. <listitem>
  203. <para> Subclasea a
  204. <classname>Zend_Controller_Dispatcher</classname>
  205. y anula el método
  206. <methodname>getAction()</methodname> para
  207. verificar si la acción existe. Como ejemplo: </para>
  208. <programlisting language="php"><![CDATA[
  209. class My_Controller_Dispatcher extends Zend_Controller_Dispatcher
  210. {
  211. public function getAction($request)
  212. {
  213. $action = $request->getActionName();
  214. if (empty($action)) {
  215. $action = $this->getDefaultAction();
  216. $request->setActionName($action);
  217. $action = $this->formatActionName($action);
  218. } else {
  219. $controller = $this->getController();
  220. $action = $this->formatActionName($action);
  221. if (!method_exists($controller, $action)) {
  222. $action = $this->getDefaultAction();
  223. $request->setActionName($action);
  224. $action = $this->formatActionName($action);
  225. }
  226. }
  227. return $action;
  228. }
  229. }
  230. ]]></programlisting>
  231. <para> El código anterior comprueba para ver que las
  232. acciones solicitadas existan en la clase del
  233. controlador; si no, se restablece la acción a la
  234. acción por defecto. </para>
  235. <para> Este método es agradable porque puede alterar
  236. transparentemente la acción antes del último
  237. despacho. Sin embargo, también significa que errores
  238. ortográficos en la <acronym>URL</acronym> todavía
  239. pueden despacharse correctamente, lo que no es muy
  240. bueno para la optimización en un motor de búsqueda.
  241. </para>
  242. </listitem>
  243. <listitem>
  244. <para> Use
  245. <methodname>Zend_Controller_Action::preDispatch()</methodname>
  246. o
  247. <methodname>Zend_Controller_Plugin_Abstract::preDispatch()</methodname>
  248. para identificar acciones inválidas. </para>
  249. <para> Subclaseando
  250. <classname>Zend_Controller_Action</classname> y
  251. modificando <methodname>preDispatch()</methodname>,
  252. puede modificar todos sus controladores que
  253. transmitan a otra acción o redireccionar antes de
  254. despachar la acción. El código para hacer esto se
  255. verá parecido al código de sustitución de arriba
  256. <methodname>__call()</methodname>. </para>
  257. <para> Alternativamente, puede verificar esta
  258. información en un plugin global. Esto tiene la
  259. ventaja de ser independiente del controlador de
  260. acción; si su aplicación consiste en una variedad de
  261. controladores de acción, y no todos ellos heredan de
  262. la misma clase, este método puede añadir coherencia
  263. a su manejo de clases diferentes. </para>
  264. <para> Como ejemplo: </para>
  265. <programlisting language="php"><![CDATA[
  266. class My_Controller_PreDispatchPlugin extends Zend_Controller_Plugin_Abstract
  267. {
  268. public function preDispatch(Zend_Controller_Request_Abstract $request)
  269. {
  270. $front = Zend_Controller_Front::getInstance();
  271. $dispatcher = $front->getDispatcher();
  272. $class = $dispatcher->getControllerClass($request);
  273. if (!$controller) {
  274. $class = $dispatcher->getDefaultControllerClass($request);
  275. }
  276. $r = new ReflectionClass($class);
  277. $action = $dispatcher->getActionMethod($request);
  278. if (!$r->hasMethod($action)) {
  279. $defaultAction = $dispatcher->getDefaultAction();
  280. $controllerName = $request->getControllerName();
  281. $response = $front->getResponse();
  282. $response->setRedirect('/' . $controllerName
  283. . '/' . $defaultAction);
  284. $response->sendHeaders();
  285. exit;
  286. }
  287. }
  288. }
  289. ]]></programlisting>
  290. <para> En este ejemplo, vamos a consultar para ver si la
  291. acción solicitada está disponible en el controlador.
  292. Si no, redireccionamos a la acción predeterminada en
  293. el controlador, y salimos inmediatamente de la
  294. ejecución del script. </para>
  295. </listitem>
  296. </itemizedlist>
  297. </listitem>
  298. </itemizedlist>
  299. </sect2>
  300. </sect1>