Excepciones MVCIntroducción
Los componentes MVC en Zend Framework utilizan un Front Controller,
lo que significa que todas las solicitudes de un determinado
sitio pasarán por un solo punto de entrada. Como resultado, todas
las excepciones burbujearán eventualmente hacia arriba hasta el
Front Controller, permitiendo al desarrollador manejarlos en un
solo lugar.
Sin embargo, los mensajes de excepción y la información de backtrace
contienen a menudo información sensible del sistema, como
declaraciones SQL, ubicaciones de archivos y otras cosas más.
Para ayudar a proteger su sitio, por defecto
Zend_Controller_Front captura todas las
excepciones y las registra con el objeto respuesta; a su vez, y
por defecto, el objeto respuesta no muestra mensajes de excepción.
Manejando las Excepciones
Ya hay varios mecanismos construidos en los componentes de MVC,
que le permiten manejar excepciones.
Por defecto, el error
handler plugin está registrado y activo.
Este plugin fue diseñado para manejar:
Errores debido a controladores o acciones perdidasErrores ocurriendo dentro de controladores de acción
Operan como un plugin de postDispatch(),
y comprueban para ver si un despachador, controlador de
acción, o de otra excepción ha ocurrido.
Si así fuera, lo remite a un controlador de manejo de
errores.
Este manejador abarcará la mayoría de las situaciones
excepcionales, y maneja airosamente controladores y acciones
perdidos.
Zend_Controller_Front::throwExceptions()
Pasando a este método un valor booleano verdadero,
puede decirle al front controller que, en lugar
de sumar excepciones en el objeto respuesta o utilizando
el plugin de manejo de errores, prefiere manejarlos usted mismo.
Como ejemplo:
throwExceptions(true);
try {
$front->dispatch();
} catch (Exception $e) {
// usted mismo maneja las excepciones
}
]]>
Este método es probablemente la forma más fácil de añadir
un manejo de excepciones personalizado que abarque toda la
gama de posibles excepciones a su aplicación de
front controller.
Zend_Controller_Response_Abstract::renderExceptions()
Al pasar a este método un valor booleano verdadero,
le esta diciendo al objeto respuesta que debe emitir un
mensaje de excepción y backtrace cuando se renderiza a sí
mismo.
En este escenario, se mostrará cualquier excepción planteada
por su aplicación. Esto no es recomendable para entornos de
producción, pero sí en desarrollo.
Zend_Controller_Front::returnResponse() y
Zend_Controller_Response_Abstract::isException().
Pasando un valor booleano verdadero a
Zend_Controller_Front::returnResponse(),
Zend_Controller_Front::dispatch()
no renderizará la respuesta, sino que la devolverá.
Una vez que tiene la respuesta, entonces puede probar ver
si todas las excepciones fueron atrapadas usando su método
isException(), y recuperando las excepciones
a través del método getException().
Como ejemplo:
returnResponse(true);
$response = $front->dispatch();
if ($response->isException()) {
$exceptions = $response->getException();
// maneje las excepciones ...
} else {
$response->sendHeaders();
$response->outputBody();
}
]]>
La principal ventaja que este método ofrece por sobre
Zend_Controller_Front::throwExceptions()
es que le permite renderizar condicionalmente la respuesta
después de manejar la excepción. Esta capturará cualquier
excepción en la cadena de controladores, a diferencia del
plugin de manejo de errores.
Excepciones MVC que Usted Pueda Encontrar
Los diversos componentes de MVC -- solicitud, router, despachador,
controlador de acción, y los objetos respuesta -- pueden arrojar
excepciones en ocasiones.
Algunas excepciones puede ser condicionalmente anuladas,
y otras se usan para indicar al desarrollador que puede necesitar
re-considerar la estructura de su aplicación.
Como algunos ejemplos:Zend_Controller_Dispatcher::dispatch()
hará, por defecto, arrojar una excepción si se hace un
requerimiento a un controlador no válido.
Hay dos maneras recomendadas para lidiar con esto.
Establecer el parámetro
useDefaultControllerAlways.
En su front controller, o en su despachador,
añada la siguiente directiva:
setParam('useDefaultControllerAlways', true);
// o
$dispatcher->setParam('useDefaultControllerAlways', true);
]]>
Cuando este flag está establecido, el despachador
utilizará el controlador y la acción por defecto
en lugar de lanzar una excepción.
La desventaja de este método es que cualquier
error ortográfico que un usuario haga cuando
acceda a su sitio lo resolverá y mostrará su
página de inicio, y que puede causar estragos
con la optimización para los motores de búsqueda.
La excepción arrojada por dispatch() es
una Zend_Controller_Dispatcher_Exception
conteniendo el texto 'Invalid controller specified'.
Use uno de los métodos descriptos de la
sección anterior para atrapar la
excepción, y luego redireccionar a una página
genérica de error o a la página de inicio.
Zend_Controller_Action::__call()
arrojará una
Zend_Controller_Action_Exception
si no puede despachar una acción inexistente a un método.
Es probable que desee utilizar alguna acción por defecto
en el controlador en casos como este.
Formas de lograr esto incluyen:
Subclasear Zend_Controller_Action y
anular el método __call().
Como ejemplo:
getRequest()->getControllerName();
$url = '/' . $controller . '/index';
return $this->_redirect($url);
}
throw new Exception('Invalid method');
}
}
]]>
El ejemplo anterior intercepta cualquier llamada a
un método de acción indefinido y redirecciona a la
acción predeterminada en el controlador.
Subclasea a Zend_Controller_Dispatcher
y anula el método getAction()
para verificar si la acción existe.
Como ejemplo:
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;
}
}
]]>
El código anterior comprueba para ver que las
acciones solicitadas existan en la clase del
controlador; si no, se restablece la acción a la
acción por defecto.
Este método es agradable porque puede alterar
transparentemente la acción antes del último
despacho. Sin embargo, también significa que errores
ortográficos en la URL todavía pueden despacharse
correctamente, lo que no es muy bueno para la
optimización en un motor de búsqueda.
Use
Zend_Controller_Action::preDispatch()
o
Zend_Controller_Plugin_Abstract::preDispatch()
para identificar acciones inválidas.
Subclaseando Zend_Controller_Action
y modificando preDispatch(),
puede modificar todos sus controladores que
transmitan a otra acción o redireccionar antes de
despachar la acción. El código para hacer esto se
verá parecido al código de sustitución de arriba
__call().
Alternativamente, puede verificar esta información
en un plugin global. Esto tiene la ventaja de ser
independiente del controlador de acción; si su
aplicación consiste en una variedad de controladores
de acción, y no todos ellos heredan de la misma clase,
este método puede añadir coherencia a su manejo de
clases diferentes.
Como ejemplo:
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;
}
}
}
]]>
En este ejemplo, vamos a consultar para ver si la
acción solicitada está disponible en el controlador.
Si no, redireccionamos a la acción predeterminada
en el controlador, y salimos inmediatamente de la
ejecución del script.