Zend_Controller-Exceptions.xml 16 KB

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