© sfriessner/Shutterstock.com
Auf dem Büffel durch die Weiten des Webs

Go Buffalo


Die Weiten des Webs lieben Go. Aber auf der Suche nach dem passenden Web-Framework kann man sich leicht verirren. Die Suche hat jetzt ein Ende, denn mit Buffalo lernt ihr das vollständigste Ökosystem für Webanwendungen mit Go kennen. Mit diesem Büffel seid ihr auf jedem Ritt durch die Weiten des Webs richtig unterwegs.

Buffalo [1] ist ein ganzes Ökosystem zur Entwicklung von Webanwendungen in Go. Routing, Templating und Testing werden von Buffalo zu einer Webanwendung nach dem Model-View-Controller-Ansatz [2] kombiniert. Die Peitsche schwingt dabei ein mächtiges Tooling mit Hot-Code-Reload, einem Codegenerator für Views und Controller und zur Automatisierung wiederkehrender Aufgaben, wie JavaScript komprimieren oder Datenbankskripte einspielen. Dazu kombiniert Buffalo bestehende Bibliotheken, wie das Webtoolkit Gorilla [3], mit Best Practices der Go-Webentwicklung, wie einer einheitlichen Struktur für Packages und Verzeichnisse oder dem Handling der Datenbank-Session. Buffalo erfindet also nichts neu, sondern kombiniert Bestehendes zu einem Ökosystem für Webanwendungen mit Go. So kann jeder Cowboy das auswählen, was er für seine Reise durch die Weiten des Webs braucht.

Cowboy George baut ein REST API für die Best Western Bank mit Postgres im Backend und nutzt das Routing und die Datenbankanbindung. Cowgirl Abby braucht Templating und die Asset-Pipeline für ihre Webanwendung zur Tischreservierung im Klondike Saloon. Jetzt aber genug der Worte, satteln wir die Pferde damit ihr Greenhorns endlich was über Buffalo lernt.

Als erste Buffalo-Anwendung bauen wir ein REST API mit Cowboy George. Als zweite Buffalo-Anwendung helfen wir Cowgirl Abby bei ihrer Webanwendung mit Frontend für den Klondike Saloon.

Let’s go, Buffalo

Bevor es losgeht, müssen wir Buffalo erst noch installieren. Das geht am einfachsten über go get -u -v github.com/gobuffalo/buffalo/buffalo oder wie in der Buffalo-Dokumentation [4] beschrieben. Jetzt sollte der Befehl buffalo auf der Kommandozeile die Buffalo-Hilfe ausgeben. Buffalo unterstützt Go Modules [5] zur Verwaltung der Package Dependencies, also aktivieren wir Go Modules mit der Umgebungsvariable GO111MODULE=on.

Cowboy Georges REST API

Als Erstes folgen wir den Spuren von Cowboy George und seines REST API für die Best Western Bank. George erstellt das Grundgerüst für seine neue Buffalo-Webanwendung mit dem Kommandozeilentool buffalo. Der Befehl buffalo new bwb --api erzeugt eine neue Buffalo-Webanwendung ohne Frontend (daher der Zusatz --api). George hat jetzt eine vollständige und lauffähige Webanwendung für seinen Kunden Best Western Bank im Verzeichnis bwb. Schauen wir uns an, was Buffalo generiert hat.

Wegweiser durch die erste Buffalo-App

Der Startpunkt einer Buffalo-Anwendung ist die Datei main.go im Wurzelverzeichnis der Anwendung. Die main.go liegt im Go Package main und startet die Anwendung, indem die Methode Serve des Buffalo Structs buffalo.App mit app.Serve() ausgeführt wird. Die Instanz der buffalo.App wird mit der Methode App() aus dem Package actions erzeugt. Im Package actions liegt die app.go, in der die eigene buffalo.App erzeugt und konfiguriert wird. In der app.go werden alle Routen der Webanwendung und alle Middlewarekomponenten registriert. Was in der app.go genau passiert, schauen wir uns im Abschnitt „Routen, Actions und Handler“ nochmal im Detail an. Außerdem liegen im Package actions alle Buffalo Actions. Die Actions bilden die Controller-Schicht des MVC-Patterns. Initial gibt es nur eine Action, den HomeHandler in der home.go mit dem dazugehörigen Test home_test.go. Die Action HomeHandler wird angesprochen, wenn ein HTTP Request die Web-Root / anfragt.

Im Package models liegt die Model-Schicht des MVC-Patterns. Buffalo geht davon aus, dass die Model-Schicht der Anwendung gegen eine Datenbank läuft, im Fall von Georges Anwendung gegen eine Postgres-Datenbank. Die Zugriffsschicht für die Datenbank basiert auf dem Buffalo-eigenen Framework pop [6].

Noch enthält die Anwendung keine eigenen Models und in der models.go wird lediglich die Verbindung zur Postgres-Datenbank initialisiert. Das Verzeichnis fixtures enthält eine Datei sample.toml, die für Tests gegen die Datenbank genutzt werden kann. Im Verzeichnis migrations liegen Skripte zur Pflege des Datenbankschemas mit dem Tool fizz [7], auch dazu später mehr. Beim Build legt Buffalo das fertige Binary im Verzeichnis bin ab, während der Entwicklung werden Daten im tmp-Verzeichnis gehalten. Daneben gibt es noch ein paar weniger wichtige Verzeichnisse wie config für die Konfiguration von Buffalo selbst und grift zur Automatisierung wiederkehrender Aufgaben mit sogenannten Tasks. Für sein REST API benötigt George vor allem die Verzeichnisse actions, models und migrations.

Neben den Verzeichnissen erzeugt Buffalo auch noch verschiedene Konfigurationsdateien. Wichtig sind hier vor allem die database.yml mit dem Datenbankzugang für verschiedene Umgebungen und das Dockerfile. Die anderen lassen wir erst einmal außen vor. Da ihr euch nun in Georges Anwendung zurechtfindet, geht es los mit der Entwicklung.

Howdy, Best Western Bank!

Die Anwendung für die Best Western Bank startet George mit buffalo dev im Entwicklungsmodus. Das REST API ist unter http://localhost:3000 erreichbar. Aber huch, da kommt ja ein Fehler. Damit es klappt, muss die Postgres-Datenbank laufen. Die startet George mit dem Docker-Befehl docker run --name bwb_db -e POSTGRES_PASSWORD=postgres -d postgres. Jetzt müsste es klappen, und der Zugriff auf die Web-Root des REST API liefert ein JSON mit der Message „Welcome to Buffalo!“ – nun aber zum Code.

Jede HTTP-Anfrage an die Anwendung landet beim Router. Der Router leitet die Anfrage dann an die zuständige Action weiter und die Action arbeitet sie ab. Die Routen einer Buffalo-Anwendung stehen in der app.go im Package actions. In der app.go wird mit app.GET("/", HomeHandler) angegeben, dass die Action HomeHandler für / (also Web-Root) zuständig ist. Die Action HomeHandler ist in der home.go im Package actions definiert. In der home.go finden wir auch den Code mit dem String Welcome to Buffalo!. Diesen String ändert George nun zu Welcome to Best Western Bank! für seinen Kunden Best Western Bank. Nach dieser Änderung am Go-Code baut Buffalo im Entwicklungsmodus automatisch die Anwendung neu und startet sie einmal durch. Der nächste Aufruf von http://localhost:3000 zeigt Georges neuen Text. So werden alle Änderungen am Code während der Entwicklung mit Buffalo direkt wirksam. Trotzdem sollte während der Entwicklung viel mit Tests gearbeitet und möglichst wenig manuell gegen die laufende Anwendung getestet werden.

Buffalo in Action

George braucht einen einfachen Health Check unter der Route /health. Dazu ruft er den Buffalo-Codegenerator mit dem Befehl buffalo generate action health check --skip-template auf. Der Generator erzeugt dann eine Buffalo Action mit Namen HealthCheck. Actions werden in Buffalo die Controller des MVC-Patterns genannt. Der erste Parameter health ist der Name der Action und der zweite Parameter check ist der Name des Handlers. Mit dem Zusatz --skip-template unterdrücken wir, dass Buffalo ein HTML-Template für unsere Action erzeugt, denn das ist für unser REST API nicht nötig. Buffalo erzeugt somit die Dateien health.go und health_test.go im Verzeichnis actions und passt die Datei app.go an.

Eine Buffalo Action muss das Interface buffalo.Handler implementieren. Actions sind in der Regel einfache Funktionen im Package actions ohne spezielle Namenskonvention. Der generierte Handler heißt deshalb HealthCheck. Die Begriffe Action und Handler werden beide synonym für MVC Controller verwendet. In der Go-Standardbibliothek wird der Begriff Handler verwendet, so auch in vielen anderen Go Frameworks für Webanwendungen.

In der Datei health.go liegen die Implementierung des Handlers und ein Test für den Handler in health_test.go. Außerdem wurde der Health Check in der app.go mit app.GET("/health/check", HealthCheck) für die Route / health/check registriert. Die Route passt noch nicht ganz, also ändert George sie in der app.go auf /health. George prüft nun den Handler HealthCheck aus health.go. Die Implementierung des Handlers besteht aus der Zeile c.Render(200, r.HTML("health/check.html")). Es soll also der HTTP-Statuscode 200 (OK) zurückgegeben und das HTML-Template templates/health/check.html gerendert werden. Der generierte Handler versucht ein HTML-Template zu rendern, obwohl wir für die Health Check Action extra kein Template generiert haben. Das kann also nicht gut gehen. George reicht für den Health Check eine Ausgabe als Plain Text, also ändert er die Implementierung in c.Render(200, r.String("Up and running!")). Jetzt läuft der Health Check unter http://localhost:3000/health und gibt den String Up and running! als Plain Text aus.

Actions können mit dem Buffalo-Generator nicht nur erzeugt, sondern auch wieder entfernt werden. Der Befehl buffalo destroy action health würde die Action health wieder entfernen. Allerdings solltet ihr danach prüfen, ob alle Routen korrekt aus der app.go entfernt wurden.

Test in Action

Noch ist George aber nicht fertig mit dem Health Check, denn der Test health_test.go schlägt noch fehl. Den Rumpf des Tests hat der Buffalo-Generator schon erzeugt, die Implementierung schreibt jetzt George. Der generierte Test ist ein spezieller Buffalo-Test mit Zugriff auf die Buffalo ActionSuite. Die ActionSuite wird in der ...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang