HTTP-Routing¶
Alvine unterstützt das Routing von HTTP-Anfragen. Darunter versteht man das Übersetzen von HTTP-Anfragen bestehend aus der Request-Methode und der übergebenen URL. Neben dem Alias können URI auch durch Ihre ID direkt aufgerufen werden. Der Webserver muß für die Verwendung der URL richtig konfiguriert sein.
Bestandteile¶
Ein HTTP-Routing besteht aus mehreren Bestandteilen, die im folgendne genauer betrachtet werden.
HTTP-Methode¶
Gültige HTTP Request Methoden für HTTP sind GET, POST, PUT, DELETE und HEAD.
URI-Muster¶
Das URI-Muster kann entweder eine feste Zeichenkette oder ein Regulärer Ausdruck sein. Reguläre Ausdrücke werden mittels der PREG-Methoden ausgewertet. Alle gültigen RegEx-Muster sind erlaubt.
Statische Pfade¶
Bei statischen Pfaden ist eine fest definierte Zeichenkette ohne regulären Ausdruck definiert. Außerdem ist diese Route auch noch über die ID aufrufbar. http://localhost/page/lists ist in diesem Fall also gleichbedeutend mit http://localhost/550e8400-e29b-11d4-a716-446655440002.
GET /page/lists \Application\App\Web\Page 550e8400-e29b-11d4-a716-446655440002
Einbetten in Templates¶
Um in der Anwendung auf die definierten URI zugreifen zu können, besteht die Möglichkeit einer Rückwärtssuche über die ID.
Pfade mit dynamischen Teilen¶
Bei Pfaden mit dynamischen Teilen kann entweder ein fester Wert, ein Wildcard oder ein regulärer Ausdruck für den dynamischen Teil verwendet werden. Der dynamische Teil muss dabei immer in geschweiften Klammern notiert werden.
GET /page/{id} \Application\App\Web\Page 550e8400-e29b-11d4-a716-446655440000
GET /page/{id}/sub/{[a-z]+name} \Application\App\Web\Page 550e8400-e29b-11d4-a716-446655440001
Priorisierung¶
Die Auswertung der Routingtabelle erfolgt von oben nach unten. Trifft eine Aussage zu, so wird diese als Anweisung an die Applikation übergeben.
Presenter¶
Der Presenter implementiert die Logik für die Darstellung des Objektes und muss über den kompletten Namen der Zielklasse definiert werden.
Zusammenspiel¶
Das Zusammenspiel der einzelnen Objekte ist im folgenden Sequenzdiagramm abgebildet.
Klasse | Typ |
---|---|
\Alvine\Application\Assembly\Web | Web |
\Alvine\Application\Web\DefaultRouter | Router |
\Alvine\Application\Web\Route | Route |
\Alvine\Application\Web\Presenter | Presenter |
\Alvine\Application\Web\View | View |
Alvine\Application\Web\Model\Generic | Model |
In der Route wird geprüft, ob es eine dem HTTP-Verb (Presenter::get(), Presenter::post(), Presenter::put(),
Presenter::post()) entsprechende Methode gibt. Ist dies nicht der Fall, so wird die Methode Presenter::execute()
aufgerufen.
Definition des Routings¶
Text-Parser¶
Eine schnelle und einfache Konfiguration erfolgt in Abhängigkeit des eingesetzten Parsers. Bei der Verwendung des Alvine\Application\Web\Router\RouteParser stehen die Routen in einer einer einfachen Textdatei. Jede Zeile in der Konfiguration startet mit der Request-Methode, gefolgt von URI-Muster und der Klasse und der Methode die Aufgerufen werden soll.
Nach der Presenter-Klasse folgt die eindeutige ID der Route.
Als letzter Teil des Routings sind die Parameter definiert.
Kommentare werden mittels #
in der Datei eingeleitet.
Weitere Routingtabellen können über das INCLUDE-Statement eingebunden werden. Das erste Argument ist dabei die Routingtabelle. Ein zweites optionales Argument ist eine Eigenschaftsdatei, die Platzhalter definiert (siehe XML-Parser).
# ROUTING-TABLE
# Laden einer Seite
GET /page/home \Application\App\Web\Page 550e8400-e29b-11d4-a716-446655440000 id:'home'
GET /page/{id} \Application\App\Web\Page 550e8400-e29b-11d4-a716-446655440001
POST /page/{id} \Application\App\Web\Page 550e8400-e29b-11d4-a716-446655440002 name:'neue Seite'
# Einbinden anderer Routingtabellen mit einer Propertydatei
INCLUDE myRouting.xml detail.properties
# Einbinden anderer Routingtabellen mit einer Propertydatei
INCLUDE myrouting.xml
# Einbinden einer Route aus einem Phar-Archive
# Das Phar-Archive muss aber bereits eingebunden (include) worden sein.
INCLUDE phar://myphar.phar/config/route/my.xml
XML-Parser¶
Eine weitaus umfassendere Konfiguration lässt sich mit dem XML-Parser realisieren. Hier stehen neben der im Abschnitt über den Text-Parser beschrieben Möglichkeiten weitere zur Verfügung. Im folgenden Beispiel ist die URI /info/ mit der ID spezifiziert. Eine weitere URI mit der ID erlaubt darüber hinaus die Angabe einer Kategorie (siehe PHP-Dokumentation).
Ein weiteres Merkmal des XML-Parsers ist die Möglichkeit Handler für Exceptions zu definieren. Im Falle eines Fehlers wird in diesem Beispiel die entsprechende Route aufgerufen.
Der Nachfolgende Code ist ein Beispiel für eine Data-Route. Die Platzhalter %{} werden dabei durch eine Property-Datei, din im include-Statement definiert wurde ersetzt.
Beispiel Routen¶
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definition>
<routes>
<group>
<route default="true" id="%{id-prefix}-LIST">
<method>GET</method>
<presenter>%{list-presenter}</presenter>
<pattern><![CDATA[^/%{endpoint}$]]></pattern>
<template>/%{endpoint}</template>
<parameters>
<parameter name="model" type='string'>%{model}</parameter>
</parameters>
<links>
<link route="%{id-prefix}-DELETE" relation="delete" label="i18n{delete}"/>
<link route="%{id-prefix}-ADD-FORM" relation="add" label="i18n{add}"/>
<link route="%{id-prefix}-UPDATE-FORM" relation="action" label="i18n{update}"/>
</links>
</route>
<route id="%{id-prefix}-DELETE">
<method>DELETE</method>
<presenter>%{delete-presenter}</presenter>
<pattern><![CDATA[^/%{endpoint}/(?<uuid>([0-9a-z\-]+)){1}$]]></pattern>
<template>/%{endpoint}/${uuid}</template>
<parameters>
<parameter name="model" type='string'>%{model}</parameter>
</parameters>
</route>
<route id="%{id-prefix}-ADD-FORM">
<method>GET</method>
<presenter>%{add-presenter}</presenter>
<pattern><![CDATA[^/%{endpoint}/add$]]></pattern>
<template>/%{endpoint}/add</template>
<parameters>
<parameter name="model" type='string'>%{model}</parameter>
</parameters>
<links>
<link route="%{id-prefix}-ADD" relation="add" id="add" label="i18n{add}"/>
<link route="%{id-prefix}-LIST" relation="action" id="back" label="i18n{list}"/>
</links>
</route>
<route id="%{id-prefix}-ADD">
<method>POST</method>
<presenter>%{add-presenter}</presenter>
<pattern><![CDATA[^/%{endpoint}$]]></pattern>
<template>/%{endpoint}</template>
<parameters>
<parameter name="model" type='string'>{model}</parameter>
</parameters>
<links>
<link route="%{id-prefix}-UPDATE-FORM" relation="redirect"/>
<link route="%{id-prefix}-UPDATE-FORM" relation="redirect-fail"/>
</links>
</route>
<route id="%{id-prefix}-UPDATE-FORM">
<method>GET</method>
<presenter>%{update-presenter}</presenter>
<pattern><![CDATA[^/%{endpoint}/(?<uuid>([0-9a-z\-]+)){1}$]]></pattern>
<template>/%{endpoint}/${uuid}</template>
<parameters>
<parameter name="model" type='string'>%{model}</parameter>
</parameters>
<links>
<link route="%{id-prefix}-UPDATE" relation="update" label="i18n{update}"/>
<link route="%{id-prefix}-LIST" relation="action" id="back" label="i18n{list}"/>
<link route="%{id-prefix}-ADD-FORM" relation="action" id="add" label="i18n{add}"/>
</links>
</route>
<route id="%{id-prefix}-UPDATE">
<method>PUT</method>
<presenter>%{update-presenter}</presenter>
<pattern><![CDATA[^/%{endpoint}/(?<uuid>([0-9a-z\-]+)){1}$]]></pattern>
<template>/%{endpoint}/${uuid}</template>
<parameters>
<parameter name="model" type='string'>%{model}</parameter>
</parameters>
<links>
<link route="%{id-prefix}-UPDATE-FORM" relation="redirect"/>
<link route="%{id-prefix}-UPDATE-FORM" relation="redirect-fail"/>
</links>
</route>
</group>
</routes>
</definition>
Gruppen¶
Jede Route in einer Gruppe bekommen eine Referenz auf die anderen Routen in der Gruppe. Diese Referenz definieren welche Methoden auf einen Entpunkt möglich sind.
Hinweis
Jede Route muss in einer Gruppe definiert sein.
Ein Anwendungsfall ist zum Beispiel bei einer Darstellung von Listen, in denen der User Objekte löschen oder ändern kann. Die URLs zum löschen oder ändern, kann dann von der Anwendung im Response direkt mitgeliefert werden.
Definitionen der Gruppen¶
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definition>
<routes>
<group>
<route id="A1">
<method>GET</method>
...
</route>
<route id="A2">
<method>POST</method>
...
</route>
<route id="A3">
<method>PUT</method>
...
</route>
<route id="A4">
<method>DELETE</method>
...
</route>
</group>
</routes>
</definition>
Links¶
Über Links lassen sich Routen definieren, die als Ausgwabspunkt der aktuellen Route genommen werden können. Wird zum Beispiel eine Liste angezeigt, besteht ausgehend von der Liste die Möglichkeit Einträge zu ändern und zu löschen.
<links>
<link route="A1" relation="delete" label="Löschen"/>
<link route="A2" relation="list" label="Liste"/>
<link route="A3" relation="update" label="Ändern"/>
<link route="A4" relation="patch" label="Patchen"/>
</links>
Attribute¶
Attribute | Beschreibung |
---|---|
relation | Schlüsselwort der Beziehung |
route | ID der Zielroute |
label | Bezeichnung des Links (z.B. im Anchor) |
Besondere Relationen¶
Beziehung | Beschreibung |
---|---|
delete | URL zum löschen der Route |
list | Liste |
update | Ändern eines Eintrages |
patch | Ändern eines Feldes |
add | Hinzufügen eines neuen Eintrages |
Default-Route¶
Wenn keine Route passt, so kann über das Attribute default="true" eine Route als Default gekennzeichnet werden. Bei dieser Route wird das Pattern nicht überprüft und die Route wird unabhängig davon ausgeführt.
<route id="B1" default="true">
<method>GET</method>
<presenter>\Alvine\Application\Web\Presenter\Home</presenter>
<pattern><![CDATA[^/$]]></pattern>
<template>/</template>
</route>
Parameter¶
Definition von Parametern¶
Dem Presenter werden im entsprechenden Aufruf nur die die Parameter die der Presenter in der Methode::getExecuteParameter()
definiert hat übergeben. Die Standardimplementierung nimmt alle Werte die in der Route innerhalb des
public function getExecuteParameter(Route $routeObj, \Alvine\Types\Map\ObjectMap $parameter, $method, \Alvine\Net\Resource\URI $route) {
$collection = new \Alvine\Types\Collection('\Alvine\Types\Parameter');
foreach($parameter as $param) {
$collection->append($param);
}
return $collection;
}
XML-Definition der Route
<parameters>
<!-- String-Parameter mit Defaultwert my -->
<parameter name="template" type="string">my</parameter>
<!-- Boolean-Parameter mit Defaultwert true -->
<parameter name="flag" type="boolean">true</parameter>
</parameters>
Auswertung der Parameter¶
Die Werte eines Parameters können aus unterschiedlichen Quellen kommen. Die Standardimplementierung prüft der Reihe nach Parameter die über die Route definiert wurden, Cookies, Werte aus dem Query, aus dem HTTP-Body und von der URI.
Hinweis
Gibt eine der Methoden einen Wert ungleich null zurück, so wird dieser Wert genommen.
Reihen- folge | Wert | Konstante | Methode im Router | Beschreibung | Beispiel |
---|---|---|---|---|---|
1 | url | PARAMETERFROMURL | getUrlParameter | Werte die über ein Matching der URL entstehen - pattern | www.example.com/mustermann/ (in der Route definiert (? |
2 | body | PARAMETERFROMBODY | getBodyParameter | alle im Body per Post übergebenen Variablen mit MIMEType Formular | name=mustermann (im HTTP-Body übertragen) |
3 | query | PARAMETERFROMQUERY | getQueryParameter | Die Werte in der URL die nach dem Fragezeichen kommen | www.example.com/?name=mustermann |
4 | coockie | PARAMETERFROMCOOKIE | getCookieParameter | alle im Cookie übertragenen Werte | name=mustermann (im Header übertragen) |
5 | route | PARAMETERFROMROUTE | getRouteParameter | die in der Route definierten Parameters |
Die verwendeten Parameter und die Reihenfolge kann über das Attribute source definiert werden. In dem folgenden Beispiel sollen nur Werte die über ein Cookie kommen, oder im Query definiert sind genommen werden.
<parameters source="cookie,query">
<!-- String-Parameter mit Defaultwert my -->
<parameter name="template" type="string">my</parameter>
<!-- Boolean-Parameter mit Defaultwert true -->
<parameter name="flag" type="boolean">true</parameter>
</parameters>
Im Presenter stehen definierten Parameter zur Verfügung. Wird in der Route keine gesonderte Angabe gemacht stehen alle Werte die über eine HTTP-Anfrage gekommen sind (post, get, cookie) und die über das Pattern definierten Parameter (url) zur Verfügung.
<route id="A3">
<method>GET</method>
<presenter>\Alvine\Application\Web\Presenter\PHPInfo</presenter>
<pattern><![CDATA[^/info/(?<category>([0-9]+))$]]></pattern>
</route>
Parameter deaktivieren¶
Soll kein Parameter überschrieben werden und der Standard vom Presenter verwendet werden , können die Parameter auch leer gelassen werden
<parameters source="" />
Template¶
Das Template wird für die Erstellung der URL zu dieser Route verwendet. Die Platzhalter in den geschweiften Klammern werden von der Anwendung ersetzt.
Bei einem Redirekt zum Beispiel, wird eine definierte Route über die ID geladen und das Template definiert die URL für die Location.
<route id="A1">
...
<template>/info/{category}</template>
...
</route>
Exception-Handling¶
Tritt im Presenter oder View während der Verarbeitung eine Exception auf, so kann in der Route auf diese Exception mit einem Handler reagiert werden. In dem Beispiel mit der Route A4.
<route id="852d38ed-1e1b-480a-9b0a-0cc3c3ad8b98">
<method>GET</method>
<presenter>\MyPackage\MyPresenter</presenter>
<pattern>^/repos/$</pattern>
<exceptions>
<exception class="\Alvine\Application\Web\Route\Exception\AccessDenied" handler="\Alvine\Application\Web\Route\Handler\Redirect">
<parameters>
<parameter name="location">http://www.example.com/login/</parameter>
</parameters>
</exception>
<exception class="\Alvine\Persistence\ObjectNotFoundException" handler="\Alvine\Application\Web\Route\Handler\InternalRedirect">
<parameters>
<parameter type="string" name="path">/notfound</parameter>
</parameters>
</exception>
</exceptions>
</route>
<route id="2258c4cc-f6bc-49d4-958f-f849f08ed431">
<method>GET</method>
<presenter>\Alvine\Service\Account\Presenter\NotFoundPresenter</presenter>
<pattern>^/notfound$</pattern>
<parameters/>
</route>
Dem Exception-Handler können Parameter zur Steuerung übergeben werden.
<parameters>
<parameter type="string" name="path">/notfound</parameter>
</parameters>
Die Handler sollten die entsprechende Typumwandlung selbstständig vornehmen. Jedoch besteht auch die Möglichkeit die Typen
direkt in der Route zu übergeben. Die entsprechenden Zieltypen sind dem Exception-Handler zu entnehmen. Gültige Werte sind:
integer
, string
, float
, boolean
und list
.
Wird im Presenter \MyPackage\MyPresenter nun eine AccessDenied-Exception geworfen, so kommt der Redirect-Handler auf die angegeben Login-Seite zum Zuge.
class MyPresenterextends \Alvine\Application\Web\Presenter {
public function get(\Alvine\Types\Map\ObjectMap $parameter) {
throw new \Alvine\Application\Web\Route\Exception\AccessDenied();
}
}
Standard-Handler für Exeptions¶
Die folgenden Handler können in der Reoutendefinition verwendet werden. Eigene Handler lassen sich einfach integrieren.
Klasse | Information | Parameter | Beschreibung | |
---|---|---|---|---|
\Alvine\Application\Web \Route\Handler\Redirect | Dieser Handler führt einen permanenten Redirect durch (StatusCode 301). Ein eigener StatusCode (z.B. 303) kann über den Parameter statuscode übergeben werden | location statuscode | Ziel-URL HTTP-Statuscode | optional |
\Alvine\Application\Web \Route\Handler\InternalRedirect | Dieser Handler führt die angegebene Route ohne vorheriges überprüfen des Musters aus. | path | Pfad auszuführenden Route | |
\Alvine\Application\Web \Route\Handler\ServerFailure | Es wird eine interne Fehlerseite für Serverfehler angezeigt. Das ist dann Nützlich, wenn ein schwerwiegender Fehler auftritt und kein Presenter oder View zurVerfügung steht. Dieser Handler sollte sparsam eingesetzt werden und besser durch einen internen Redirect ersetzt werden. | message | Fehlermeldung | optional |
Hinweis
Handler sollten nicht zur Ausgabe von Inhalten "missbraucht" werden.
Routenklasse¶
Die Standardroute stellt eine Vielzahl von Möglichkeiten bereit. Möchte man jedoch Änderungen am Verhalten der Route vornehmen, so kann man eine eigene Route-Klasse über den Parameter class im Route-Tag definieren. Die eigene Route-Klasse muss aber von Route abgeleitet sein.
<route id="A1" class="MyRoute">
<method>GET</method>
<presenter>\Alvine\Application\Web\Presenter\PHPInfo</presenter>
</route>
Die PHP-Klasse sieht dann folgendermaßen aus:
class MyRoute extends \Alvine\Application\Web\Route {
}
Überschreiben der HTTP-Methode¶
Da bei HTML-Formularen nur GET und POST möglich sind, kann die HTTP-Methode mit verschiedene Techniken überschrieben werden.
http://www.w3.org/html/wg/drafts/html/master/forms.html#attributes-for-form-submission
Header¶
X-HTTP-Method-Override: PUT > // Google/GData X-HTTP-Method: PUT > // Microsoft X-Method-Override: PUT > // IBM
Paramter¶
Mit dem Attribute "name = X_HTTP_METHOD_OVERRIDE " als hidden Feld in einem Formular kann die Methode überschrieben werden. Hierzu muss das Formular mit POST übergeben werden.
Beispiel:
<form method="post" action="/index.php">
<input type="hidden" name="X_HTTP_METHOD_OVERRIDE" value="DELETE" >
<input type="input" name="test">
<input type=submit>
</form>
Hinweis
Der Parameter muss nicht in der Route definiert werden.