1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
|
<?xml version="1.0" encoding="UTF-8"?>
<!-- EN-Revision: 24249 -->
<!-- Reviewed: no -->
<sect1 id="zend.controller.exceptions">
<title>MVC Ausnahmen</title>
<sect2 id="zend.controller.exceptions.introduction">
<title>Einführung</title>
<para>
Die <acronym>MVC</acronym> Komponenten im Zend Framework verwenden einen Front
Controller, was bedeutet das alle Anfragen zu einer bestimmten Site über einen
einzelnen Eintrittspunkt laufen. Als Ergebnis treten alle Ausnahmen eventuell am Front
Controllerauf, was es Entwicklern erlaubt diese an einem einzelnen Ort zu behandeln.
</para>
<para>
Trotzdem enthalten Ausnahmemeldungen und Backtrace Informationen oft sensitive
Systeminformationen, wie <acronym>SQL</acronym> Anweisungen, Dateiorte, und andere. Um
zu helfen die eigene Site zu schützen, werden standardmäßig alle Ausnahmen von
<classname>Zend_Controller_Front</classname> gefangen und im Antwortobjekt registriert;
zusätzlich zeigt das Antwortobjekt die Ausnahmemeldungen standardmäßig nicht an.
</para>
</sect2>
<sect2 id="zend.controller.exceptions.handling">
<title>Behandeln von Ausnahmen</title>
<para>
Verschiedene Mechanismen sind bereits in die <acronym>MVC</acronym> Komponenten
eingebaut um die Behandlung von Ausnahmen zu erlauben.
</para>
<itemizedlist>
<listitem>
<para>
Standardmäßig ist das
<link linkend="zend.controller.plugins.standard.errorhandler">Error Handler
Plugin</link> registriert und aktiv. Dieses Plugin wurde erstellt um
folgendes zu behandeln:
</para>
<itemizedlist>
<listitem><para>Fehler durch fehlende Controller oder Aktionen</para></listitem>
<listitem><para>Fehler die in Actioncontrollern auftreten</para></listitem>
</itemizedlist>
<para>
Es arbeitet als <methodname>postDispatch()</methodname> Plugin und prüft ob
eine Dispatcher, Actioncontroller oder andere Ausnahme aufgetreten ist. Wenn
das so ist, leitet es an den Error Handler Controller weiter.
</para>
<para>
Dieser Handler deckt die meisten Ausnahmesituationen ab, und behandelt fehlende
Controller und Aktionen taktvoll.
</para>
</listitem>
<listitem>
<para><methodname>Zend_Controller_Front::throwExceptions()</methodname></para>
<para>
Durch die Übergabe eines boolschen <constant>TRUE</constant> Wertes an diese
Methode, kann dem Front Controller mitgeteilt werden das, statt der Ansammlung
der Ausnahmen im Antwortobjekt oder der Verwendung des Error Handler Plugin's,
man diese Ausnahmen selbst behandeln will. Als Beispiel:
</para>
<programlisting language="php"><![CDATA[
$front->throwExceptions(true);
try {
$front->dispatch();
} catch (Exception $e) {
// Ausnahmen selbst behandeln
}
]]></programlisting>
<para>
Diese Methode ist möglicherweise der einfachste Weg um eigene
Ausnahmebehandlungen hinzuzufügen die den vollen Umfang der möglichen Ausnahmen
der Front Controller Anwendung behandeln.
</para>
</listitem>
<listitem>
<para>
<methodname>Zend_Controller_Response_Abstract::renderExceptions()</methodname>
</para>
<para>
Durch die Übergabe eines boolschen <constant>TRUE</constant> Wertes an diese
Methode kann dem Antwortobjekt mitgeteilt werden das es Ausnahmenachrichten und
Backtrace darstellen soll, wenn es selbst dargestellt wird. In diesem Szenario
wird jede Ausnahme die an der Anwendung auftritt angezeigt. Das wird nur in
nicht-produktiven Umgebungen vorgeschlagen.
</para>
</listitem>
<listitem>
<para>
<methodname>Zend_Controller_Front::returnResponse()</methodname> und
<methodname>Zend_Controller_Response_Abstract::isException()</methodname>.
</para>
<para>
Durch die Übergabe eines boolschen <constant>TRUE</constant> an
<methodname>Zend_Controller_Front::returnResponse()</methodname>, wird
<methodname>Zend_Controller_Front::dispatch()</methodname> die Antwort nicht
darstellen, aber diese stattdessen zurückgeben. Sobald man die antwort hat,
kann diese getestet werden um zu sehen ob irgendwelche Ausnahmen gefangen
wurden indem die <methodname>isException()</methodname> Methode verwendet, und
die Ausnahme über die <methodname>getException()</methodname> Methode empfangen
wird. Als Beispiel:
</para>
<programlisting language="php"><![CDATA[
$front->returnResponse(true);
$response = $front->dispatch();
if ($response->isException()) {
$exceptions = $response->getException();
// Ausnahme behandeln ...
} else {
$response->sendHeaders();
$response->outputBody();
}
]]></programlisting>
<para>
Der primäre Vorteil welche diese Methode über
<methodname>Zend_Controller_Front::throwExceptions()</methodname> bietet ist,
das Sie es erlaubt die Antwort wahlweise darzustellen nachdem die Ausnahme
behandelt wurde. Das fängt jede Ausnahme in der Controllerkette, im Gegensatz
zum Error Handler Plugin.
</para>
</listitem>
</itemizedlist>
</sect2>
<sect2 id="zend.controller.exceptions.internal">
<title>MVC Ausnahme die auftreten können</title>
<para>
Die verschiedenen <acronym>MVC</acronym> Komponenten -- Anfragen, Router, Dispatcher,
Actioncontroller, und Antwortobjekte -- können alle gelegentlich Ausnahmen werfen.
Einige Ausnahmen können wahlweise überschrieben werden und andere werden Verwendet um
dem Entwickler anzuzeigen das die eigene Struktur der Anwendung überdacht werden
sollte.
</para>
<para>Einige Beispiele:</para>
<itemizedlist>
<listitem>
<para>
<methodname>Zend_Controller_Dispatcher::dispatch()</methodname> wird
standardmäßig eine Ausnahme werfen wenn ein ungültiger Controller angefragt
wird. Es gibt zwei empfohlene Wege um damit umzugehen.
</para>
<itemizedlist>
<listitem>
<para>
Den <property>useDefaultControllerAlways</property> Parameter setzen.
</para>
<para>
Im eigenen Frontcontroller, oder dem eigenen Dispatcher, die folgende
Anweisung hinzufügen:
</para>
<programlisting language="php"><![CDATA[
$front->setParam('useDefaultControllerAlways', true);
// oder
$dispatcher->setParam('useDefaultControllerAlways', true);
]]></programlisting>
<para>
Wenn dieses Flag gesetzt ist, wird der Dispatcher den Standardcontroller
und die Standardaktion verwenden statt eine Ausnahme zu werfen. Der
Nachteil dieser Methode ist das jegliche Schreibfehler die ein Benutzer
macht wenn er auf die Site zugreift, trotzdem aufgelöst werden und die
Homepage angezeigt wird, was bei der Optimierung von Suchmaschienen
verherenden Schaden anrichten kann.
</para>
</listitem>
<listitem>
<para>
Die Ausnahme die von <methodname>dispatch()</methodname> geworfen wird,
ist eine <classname>Zend_Controller_Dispatcher_Exception</classname>
die den Text 'Invalid controller specified' enthält. Eine der Methoden
die in <link linkend="zend.controller.exceptions.handling">der
vorhergehenden Sektion</link> beschrieben wurden können verwendet
werden um die Ausnahme zu fangen und dann zu einer generellen
Fehlerseite oder der Homepage umzuleiten.
</para>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<para>
<methodname>Zend_Controller_Action::__call()</methodname> wird eine
<classname>Zend_Controller_Action_Exception</classname> geworfen wenn eine nicht
existierende Aktion einer Methode nicht dargestellt werden kann. Normalerweise
wird es gewünscht sein in Fällen wie diesen eine Standardaktion im Controller zu
verwenden. Wege um das zu tun beinhalten:
</para>
<itemizedlist>
<listitem>
<para>
Eine Subklasse von <classname>Zend_Controller_Action</classname>
erstellen und die <methodname>__call()</methodname> Methode
überschreiben. Als Beispiel:
</para>
<programlisting language="php"><![CDATA[
class My_Controller_Action extends Zend_Controller_Action
{
public function __call($method, $args)
{
if ('Action' == substr($method, -6)) {
$controller = $this->getRequest()->getControllerName();
$url = '/' . $controller . '/index';
return $this->_redirect($url);
}
throw new Exception('Ungültige Methode');
}
}
]]></programlisting>
<para>
Das obige Beispiel fängt jede nicht definierte Aktionsmethode ab die
aufgerufen wird und leitet Sie zur Standardaktion im Controller um.
</para>
</listitem>
<listitem>
<para>
Eine Subklasse von <classname>Zend_Controller_Dispatcher</classname>
erstellen und die <methodname>getAction()</methodname> Methode
überschreiben um zu prüfen ob die Aktion existiert. Als Beispiel:
</para>
<programlisting language="php"><![CDATA[
class My_Controller_Dispatcher extends Zend_Controller_Dispatcher
{
public function getAction($request)
{
$action = $request->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;
}
}
]]></programlisting>
<para>
Der obige Code prüft ob die angefragte Aktion in der Controllerklasse
existiert ; wenn nicht wird die Aktion auf die Standardaktion
zurückgesetzt.
</para>
<para>
Diese Methode ist nützlich, weil Sie die Aktion transparent ändert bevor
diese letztendlich dargestellt wird. Trotzdem bedeutet es auch, das
Schreibfehler in der <acronym>URL</acronym> trotzdem richtig dargestellt
werden, was für die Optimierung von Suchmaschinen nicht gut ist.
</para>
</listitem>
<listitem>
<para>
Verwenden von
<methodname>Zend_Controller_Action::preDispatch()</methodname> oder
<methodname>Zend_Controller_Plugin_Abstract::preDispatch()</methodname>
um eine ungültige Aktion zu identifizieren.
</para>
<para>
Durch das Erstellen einer Subklasse von
<classname>Zend_Controller_Action</classname> und dem modifizieren von
<methodname>preDispatch()</methodname>, können alle eigenen Controller
geändert werden damit Sie an andere Aktionen weiterleiten oder umleiten
bevor die Aktion letztendlich dargestellt wird. Der Code hierfür schaut
ähnlich wie der Code für das Überschreiben von
<methodname>__call()</methodname> aus, der oben schon angezeigt wurde.
</para>
<para>
Alternativ kann diese Information in einem globalen Plugin geprüft
werden. Das hat den Vorteil das es unabhängig von Actioncontroller ist;
wenn die eigene Anwendung aus einer Reihe von Actioncontrollern besteht,
und nicht alle von der gleichen Klasse abgeleitet sind, kann diese
Methode Kontinuität in der Handhabung der verschiedenen Klassen bringen.
</para>
<para>
Als Beispiel:
</para>
<programlisting language="php"><![CDATA[
class My_Controller_PreDispatchPlugin extends Zend_Controller_Plugin_Abstract
{
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$front = Zend_Controller_Front::getInstance();
$dispatcher = $front->getDispatcher();
$class = $dispatcher->getControllerClass($request);
if (!$class) {
$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;
}
}
}
]]></programlisting>
<para>
In diesem Beispiel wird geprüft ob die angefragte Aktion im Controller
vorhanden ist. Wenn dem nicht so ist, wird auf die Standardaktion im
Controller umgeleitet und die Ausführung des Sktipts sofort beendet.
</para>
</listitem>
</itemizedlist>
</listitem>
</itemizedlist>
</sect2>
</sect1>
|