Wyjątki MVCWprowadzenie
Komponenty MVC w Zend Framework używają kontrolera frontowego, co
oznacza, że wszystkie żądania do danej strony przechodzą przez
pojedynczy punkt. W rezultacie wszystkie wyjątki ostatecznie
zbierane są w kontrolerze frontowym, pozwalając programiście na
obsłużenie ich w jednym miejscu.
Jakkolwiek, wiadomości o wyjątkach oraz informacje o backtrace
często zawierają wrażliwe informacje o systemie, jak np. zapytania
SQL, informacje o lokacjach plików i wiele innych. Aby pomóc ci
chronić swój serwis, domyślnie Zend_Controller_Front
łapie wszystkie wyjątki i rejestruje je w obiekcie odpowiedzi; z
kolei, obiekt odpowiedzi domyślnie nie wyświetla wiadomości o
wyjątkach.
W jaki sposób możesz obsługiwać wyjątki?
Obecnie w komponentach MVC wbudowanych jest kilka mechanizmów
pozwalających na obsługę wyjątków
Domyślnie rejestrowana i aktywna jest wtyczka
obsługi błędów. Ta wtyczka została stworzona aby
obsługiwać:
Błędy spowodowane brakującym kontrolerem lub akcjąBłędy występujące wewnątrz akcji kontrolerów
Wtyczka działa w oparciu o metodę postDispatch(),
i sprawdza czy obiekt uruchamiający, kontroler akcji, lub
inny obiekt wyrzucił wyjątek. Jeśli tak, przekazuje ona
żądanie do kontrolera obsługi błędu.
Ta wtyczka obsłuży większość sytuacji, w których został
wyrzucony wyjątek, a także poprawnie obsłuży brakujące
kontrolery oraz akcje.
Zend_Controller_Front::throwExceptions()
Przekazująć logiczną wartość true do tej metody, możesz
nakazać kontrolerowi frontowemu aby zamiast składować
wyjątki w obiekcie odpowiedzi, wyrzucił je, żebyś mógł
obsłużyć je samodzielnie. Na przykład:
throwExceptions(true);
try {
$front->dispatch();
} catch (Exception $e) {
// sam obsłuż wyjątki
}
]]>
Ta metoda jest najprawdopodobniej najłatwiejszym sposobem
dodania własnej obsługi wyjątków do twojej aplikacji
używającej kontrolera frontowego.
Zend_Controller_Response_Abstract::renderExceptions()
Przekazując logiczną wartość true do tej metody, możesz
nakazać obiektowi odpowiedzi aby renderował on wyjątki gdy
sam będzie renderowany. W takim scenariuszu, każdy wyjątek
wyrzucony w twojej aplikacji będzie wyświetlony. To jest
jedynie rekomendowane dla nieprodukcyjnych środowisk.
Zend_Controller_Front::returnResponse() oraz
Zend_Controller_Response_Abstract::isException()
Przekazanie wartości logicznej true do metody
Zend_Controller_Front::returnResponse(),
spowoduje, że obiekt
Zend_Controller_Front::dispatch() nie będzie
renderował odpowiedzi, a zamiast tego ją zwróci. Gdy już
masz odpowiedź, możesz sprawdzić czy są w niej wyjątki
używając metody isException(), a następnie
odebrać wyjątki używając metody getException().
Na przykład:
returnResponse(true);
$response = $front->dispatch();
if ($response->isException()) {
$exceptions = $response->getException();
// obsługa wyjątków ...
} else {
$response->sendHeaders();
$response->outputBody();
}
]]>
Główną zaletą, dzięki której ta metoda umożliwia więcej niż
Zend_Controller_Front::throwExceptions(), jest
to, że możesz warunkowo wyświetlać odpowiedź po obsłudze
wyjątków.
Wyjątki MVC które możesz napotkać
Różne komponenty MVC -- obiekt żądania, router, obiekt uruchamiający,
kontrolery akcji, oraz obiekt odpowiedzi -- każdy może z różnych
przyczyn wyrzucać wyjątki. Niektóre wyjątki mogą być warunkowo
nadpisane, a inne są używane aby wskazać programiście potrzebę
poprawienia aplikacji.
Kilka przykładów:Zend_Controller_Dispatcher::dispatch()
domyślnie wyrzuci wyjątek jeśli zażądano nieprawidłowego
kontrolera. Są dwa zalecane sposoby na obsłużenie tego:
Ustawienie parametru useDefaultControllerAlways.
W twoim kontrolerze frontowym, lub w obiekcie
uruchamiającym, dodaj poniższą dyrektywę:
setParam('useDefaultControllerAlways', true);
// lub
$dispatcher->setParam('useDefaultControllerAlways', true);
]]>
Gdy ta flaga jest ustawiona, obiekt uruchamiający,
użyje domyślnego kontrolera oraz akcji zamiast
wyrzucania wyjątku. Minusem użycia tej metody jest
to, że jakikolwiek błąd literowy w adresie
dostępowym do twojej strony spowoduje wyświetlenie
strony głównej, co może źle wpłynąć na optymalizację
serwisu dla wyszukiwarek internetowych.
Wyjątek wyrzucany przez metodę dispatch()
jest wyjątkiem Zend_Controller_Dispatcher_Exception
zawierającym tekst 'Invalid controller specified'.
Użyj jednej z metod opisanych w poprzedniej
sekcji aby złapać wyjątek, a następnie
przekierować do strony błędu lub do strony głownej.
Metoda Zend_Controller_Action::__call() wyrzuci
wyjątek Zend_Controller_Action_Exception
jeśli nie może uruchomić nieistniejącej metody akcji.
Najczęściej będziesz chciał użyć jakiejś domyślnej akcji
w kontrolerze w tego typu sprawach. Przykładowe metody
za pomocą których możesz to osiśgnąć:
Rozszerzenie klasy Zend_Controller_Action
i nadpisanie metody __call(). Na przykład:
getRequest()->getControllerName();
$url = '/' . $controller . '/index';
return $this->_redirect($url);
}
throw new Exception('Invalid method');
}
}
]]>
Powyższa metoda przechwytuje wszystkie wywołane
niezdefiniowane akcje i przekierowuje żądanie do
domyślnej akcji w kontrolerze.
Rozszerzenie klasy Zend_Controller_Dispatcher o
nadpisanie metody getAction(), która
sprawdza czy akcja istnieje. Na przykład:
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;
}
}
]]>
Powyższy kod sprawdza czy zażądana akcja istnieje w
klasie kontrolera; jeśli nie, resetuje akcję do
akcji domyślnej.
Ta metoda jest wygodna ponieważ możesz w niewidoczny
sposób zmienić akcję przed ostatecznym uruchomieniem.
Jednak to także oznacza, że jakikolwiek błąd literowy
w adresie URL może wciąż uruchomić żądanie poprawnie,
co nie jest zbyt dobre dla optymalizacji dla
wyszukiwarek internetowych.
Użycie metody Zend_Controller_Action::preDispatch()
lub Zend_Controller_Plugin_Abstract::preDispatch()
do zidentyfikowania nieprawidłowych akcji.
Rozszerzając klasę Zend_Controller_Action i
modyfikując metodę preDispatch(), możesz
zmodyfikować wszystkie twoje kontrolery w taki
sposób, aby przenosiły one żądanie do innej akcji
lub przekierowywały zamiast uruchamiać akcję.
Kod wyglądałby podobnie kod nadpisujący metodę
__call(), który został przedstawiony wyżej.
Alternatywnie, możesz sprawdzać te informacje we
wtyczce globalnej. Zaletą tego rozwiązania jest to,
że kontroler akcji staje się niezależny; jeśli twoja
aplikacja składa się z różnorodnych kontrolerów
akcji i nie wszystkie dziedziczą z tej samej klasy,
ta metoda może dodać konsekwencji w obsłudze różnych
klas.
Przykład:
getDispatcher();
$controller = $dispatcher->getController($request);
if (!$controller) {
$controller = $dispatcher->getDefaultControllerName($request);
}
$action = $dispatcher->getAction($request);
if (!method_exists($controller, $action)) {
$defaultAction = $dispatcher->getDefaultAction();
$controllerName = $request->getControllerName();
$response = Zend_Controller_Front::getInstance()->getResponse();
$response->setRedirect('/' . $controllerName . '/' . $defaultAction);
$response->sendHeaders();
exit;
}
}
}
]]>
W tym przykładzie sprawdzamy czy zażądana akcja
jest dostępna w kontrolerze. Jeśli nie,
przekierujemy żądanie do domyślnej akcji w
kontrolerze, i kończymy wykonywanie skryptu.