Routing & Handlers
Die Yesod-Typklasse
Bereits in den Grundlagen wurde die so genannte Foundation
angesprochen, auf der das Framework basiert. Diese wird durch eine Instanz der Yesod-Typklasse
definiert. Die Yesod-Typklasse bildet eine zentrale Anlaufstelle für globale Einstellungen für die
Webanwendung. Weiterhin stellt sie hilfreiche Funktionen zur Verfügung, deren Überschreibung teilweise
von Nutzen ist. Die einzige Funktion, die nicht vordefiniert, sondern vom Nutzer selbst anzugeben ist,
ist approot
, welche bereits im einführenden Beispiel aufgeführt
wurde.[1]
1instance Yesod MyApplication where
2 approot _ = ""
Diese Funktion bestimmt die Application-Root, welche von Yesod benötigt wird, um aus typischeren
URLs String-Repräsentationen in absoluter Form zu
generieren. Sämtliche für den Frontcontroller definierten relativen Routen beziehen sich demnach auf
diese Application-Root. Der irrelevant erscheinende Parameter für die approot
-Funktion
rührt daher, dass die Angabe der Application-Root beispielsweise aus einer Konfigurationsdatei
geladen werden könnte. In diesem Fall könnte der Wert in der Foundation gespeichert werden und
die approot
-Funktion würde diesen Wert von dort
beziehen.[2]
Ein gutes Beispiel für eine im Rahmen der Yesod-Typklasse vordefinierte Funktion ist
defaultLayout
, welche bereits im Kapitel
3.1. Templates zur Sprache gekommen ist. Die folgende
Definition von defaultLayout
soll den Mechanismus zur Gestaltung eines
standardmäßigen Layouts für das Frontend verdeutlichen.
1defaultLayout contents = do
2 PageContent title headTags bodyTags <- widgetToPageContent $ do
3 addCassius [cassius|
4#body
5 font-family: sans-serif
6#wrapper
7 width: 760px
8 margin: 0 auto
9|]
10 addWidget contents
11 hamletToRepHtml [hamlet|
12!!!
13
14<html>
15 <head>
16 <title>#{title}
17 ^{headTags}
18 <body>
19 <div id="wrapper">
20 ^{bodyTags}
21|]
Zwischen den Zeilen 3 und 10 werden zu den an die Funktion übergebenen Inhalten im Rahmen
der Zugriffskomponenten von Widgets zunächst generelle
CSS-Angaben beigefügt. Das resultierende Widget
wird in der Folge in Zeile 2 in die Komponenten «Titel», «Tags im
<head>
-Kontext» und «Tags im <body>
-Kontext» entpackt.
Schließlich erfolgt in Zeile 11 bis 21 eine weitere Nutzung dieser Komponenten, indem sie als
Inhalte in den HTML-Dokumentbaum eingepflegt
werden. Die Ausrufezeichen in Zeile 12 haben die Einfügung eines
HTML-Doctypes zur Folge. Standardmäßig
handelt es sich dabei um die Definition <!DOCTYPE html>
zur Kennzeichnung von
HTML
5.[3]
Routing bei Yesod
Im Rahmen der Betrachtung des einleitenden Beispiels aus den Grundlagen wurde bereits der so genannte Front Controller beim Routing in Yesod vorgestellt. Er definiert sich zum einen aus den beschriebenen Routen und zum anderen aus den entsprechend zuständigen Handlern. Es gibt im Kontast dazu allgemein noch zwei andere Herangehensweisen zum Routen von Anfragen. Dies ist einerseits die Möglichkeit der Weiterleitung je nach Name der Datei, welche angesprochen wurde. Diese Methode ist beispielsweise bei PHP und ASP.NET gängig. Die zweite Methode zum Routen von Anfrage wird u. a. von Django und Rails verfolgt. Hierbei erfolgt die Weiterleitung über eine zentrale Funktion, welche anhand von regulären Ausdrücken die verschiedenen Routen definiert.
Der Ansatz von Yesod ist der zweiten angesprochenen Herangehensweise zum Routen von Anfragen ähnlicher. Während jedoch dort reguläre Ausdrücke zum Einsatz kommen, werden bei Yesod lediglich die Einzelteile einer Route betrachtet, welche vom Entwickler bei der Definition von Routen anzugeben sind. Der entscheidende Vorteil von Yesod gegenüber dem Ansatz, bei dem reguläre Ausdrücke verwendet werden, ist jedoch ein anderer. Im Kontext der meisten herkömmlichen Frameworks besteht nämlich nur eine One-Way-Verbindung vom Router zum zugehörigen Handler. Durch die Definition eines Route-Datentyps in Haskell ist es Yesod jedoch möglich, eine beidseitige Verbindung zwischen den Routen und der weiteren Applikationslogik herzustellen, welche sich üblicherweise im Rahmen der entsprechenden Handler befindet. An dieser Stelle zeigen wieder die typsicheren URLs ihre Stärke. Dies impliziert, dass jede Route anhand ihres entsprechenden Bezeichners eindeutig identifiziert werden kann. Die technische Implementierung des Routingsystems in Yesod wird automatisch generiert. Auch hier hilft Template Haskell bzw. dessen Quasi-Quotations, welche es Entwicklern ermöglicht, nur die gültigen Routen der Webanwendung anzugeben. Durch Anwendung des Quoters «parseRoutes» erfolgt während der Kompilierung die automatische Generierung des zum Aufbau des Routingsystems benötigten Haskellcodes.[4]
Argumente innerhalb von Routes
Bei der Definition von Routen können insgesamt drei Angabemöglichkeiten unterschieden werden.[5]
- Statisch - entspricht der statischen Angabe einer URL ohne jede Argumente,
- Dynamisch, Einzel - entspricht der Angabe von einzelnen Argumenten innerhalb einer Route, welche sich üblicherweise zwischen zwei Schrägstrichen befinden und mithilfe des Rautensymbols # gekennzeichnet werden,
- Dynamisch, Multi - wird durch das Zeichen * am Ende einer Route definiert und entspricht der Angabe von mehreren Argumenten, welche in Form einer Liste an den Handler übergeben werden
Der folgende Code enthält jeweils ein Beispiel für jede dieser Möglichkeiten zur Angabe von Routes.
1mkYesod "RouteExample" [parseRoutes|
2/home HomeR GET
3/book/#Author/#Title BookR GET
4/uploads/*Files FileR GET
5|]
Zeile 3 enthält die Angabe zweier Argumente #Author
und #Title
. Diese
Bezeichnungen kennzeichnen nicht etwa die Argumentnamen, sondern die Datentypen der Argumente. Dies
bedeutet, dass nur Anfragen ausgewertet werden, welche gültige Argumente enthalten. Über die
Definition eigener Datentypen mitsamt entsprechender Funktionen zur Konvertierung von und zu Text kann
auf diese Weise sichergestellt werden, dass dem Handler nur gültige Argumente übermittelt
werden.
Die GHandler-Monade
GHandler-Monaden bilden den Kontext, in welchem sich die Webapplikation bei der Bearbeitung
in Handlern befindet und entsprechen somit dem eigentlichen Controller. Sie beinhalten einen Reader
zur Auslesung von Daten aus der Anfrage an den Server wie beispielsweise Headerinformationen. Außerdem
steht ein Writer bereit, welcher in der Lage ist, Headerinformation der Antwort an den Client
hinzuzufügen. Eine Zustandskomponente sorgt zudem für die Verwaltung von Sitzungsvariablen. Durch
Instanzierung von monadIO
ist es möglich, über liftIO
sämtliche IO-Aktionen
durchzuführen.[6] Die GHandler-Monade
ist anhand der Signatur
1data GHandler sub master a
beschrieben, wobei sub
für den Foundation-Datentyp einer Subsite und master
für den Foundation-Datentyp derer Mastersite steht. Das Resultat eines jeden Handlers besteht aus der
Antwort, welche an den Client gesendet wird. Dies ist üblicherweise RepHtml
, kann aber
für einfachen Klartext ebenso RepPlain
sein. Bislang wurden ausnahmslos Anwendungsfälle
dargestellt, bei denen keine gesonderte Subsite existiert hat. Die Subsite entsprach dabei ihrer
Mastersite. Bei einer beispielhaften Foundation-Bezeichnung MyApp
wäre die entsprechende
GHandler-Signatur zwecks Ausgabe von HTML
GHandler MyApp MyApp RepHtml
gewesen. Für diese Konstellation empfiehlt sich die Nutzung
eines Alias type Handler = GHandler MyApp MyApp
, um eine resultierende übersichtlichere
Signatur Handler RepHtml
zu ermöglichen. Die Typsignatur eines Handlers für die in Zeile
3 des gezeigten Routensystembeispiels definierte Route könnte beispielsweise so aussehen.
1getBookR :: Author -> Title -> Handler RepHtml
Subsites sind sinnvoll, um die unterschiedlichen Aufgabenbereiche einer Webanwendung zu kapseln. Beispielsweise könnten eigene Subsites für den Zweck der Authentifizierung oder für den Zugriff auf statische Inhalte wie Grafiken und Downloads definiert werden. Subsites werden anhand eines eigenen Routingsystems definiert.[7] Nähere Informationen zu diesem Thema finden sich im Yesod Web in einem eigenen Kapitel zum Aufbau von Subsites.
- [1] Michael Snoyman, Yesod Web Framework Book: Yesod Typeclass, Introduction http://www.yesodweb.com/show/topic/132
- [2] Michael Snoyman, Yesod Web Framework Book: Yesod Typeclass, Rendering and Parsing URLs http://www.yesodweb.com/show/topic/171
- [3] Michael Snoyman, Yesod Web Framework Book: Yesod Typeclass, defaultLayout http://www.yesodweb.com/show/topic/172
- [4] Michael Snoyman, Yesod Web Framework Book: Routing and Handlers, Introduction http://www.yesodweb.com/show/topic/133
- [5] Michael Snoyman, Yesod Web Framework Book: Routing and Handlers, Types of Pieces http://www.yesodweb.com/show/topic/135
- [6] Michael Snoyman, Yesod Web Framework Book: Routing and Handlers, The GHandler Monad http://www.yesodweb.com/show/topic/143
- [7] Michael Snoyman, Yesod Web Framework Book: Creating a subsite http://www.yesodweb.com/book/subsite