Zend_Controller-Exceptions.xml 18 KB

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