Martini ist ein mächtiges Package zur schnellen Entwicklung von modularen Webanwendungen und -services in Golang.
Nach der Installation von Go und dem Einrichten des GOPATH, erstelle Deine erste .go
-Datei. Speichere sie unter server.go
.
package main
import "github.com/go-martini/martini"
func main() {
m := martini.Classic()
m.Get("/", func() string {
return "Hallo Welt!"
})
m.Run()
}
Installiere anschließend das Martini Package (Go 1.1 oder höher wird vorausgesetzt):
go get github.com/go-martini/martini
Starte den Server:
go run server.go
Der Martini-Webserver ist nun unter localhost:3000
erreichbar.
Abonniere den Emailverteiler
Schaue das Demovideo
Stelle Fragen auf Stackoverflow mit dem Martini-Tag
GoDoc Dokumentation
- Sehr einfach nutzbar
- Nicht-intrusives Design
- Leicht kombinierbar mit anderen Golang Packages
- Ausgezeichnetes Path Matching und Routing
- Modulares Design - einfaches Hinzufügen und Entfernen von Funktionen
- Eine Vielzahl von guten Handlern/Middlewares nutzbar
- Großer Funktionsumfang mitgeliefert
- Voll kompatibel mit dem http.HandlerFunc Interface.
- Standardmäßiges Ausliefern von Dateien (z.B. von AngularJS-Apps im HTML5-Modus)
Mehr Informationen zur Middleware und Funktionalität findest Du in den Repositories der martini-contrib Gruppe.
Einen schnellen Start in ein Projekt ermöglicht martini.Classic(), dessen Voreinstellungen sich für die meisten Webanwendungen eignen:
m := martini.Classic()
// ... Middleware und Routing hier einfügen
m.Run()
Aufgelistet findest Du einige Aspekte, die martini.Classic() automatich berücksichtigt:
- Request/Response Logging - martini.Logger
- Panic Recovery - martini.Recovery
- Static File serving - martini.Static
- Routing - martini.Router
Handler sind das Herz und die Seele von Martini. Ein Handler ist grundsätzlich jede Art von aufrufbaren Funktionen:
m.Get("/", func() {
println("Hallo Welt")
})
Wenn ein Handler Rückgabewerte beinhaltet, übergibt Martini diese an den aktuellen http.ResponseWriter in Form eines String:
m.Get("/", func() string {
return "Hallo Welt" // HTTP 200 : "Hallo Welt"
})
Die Rückgabe eines Statuscode ist optional:
m.Get("/", func() (int, string) {
return 418, "Ich bin eine Teekanne" // HTTP 418 : "Ich bin eine Teekanne"
})
Handler werden per Reflection aufgerufen. Martini macht Gebrauch von Dependency Injection, um Abhängigkeiten in der Argumentliste von Handlern aufzulösen. Dies macht Martini komplett inkompatibel mit Golangs http.HandlerFunc
Interface.
Fügst Du einem Handler ein Argument hinzu, sucht Martini in seiner Liste von Services und versucht, die Abhängigkeiten via Type Assertion aufzulösen.
m.Get("/", func(res http.ResponseWriter, req *http.Request) { // res und req wurden von Martini injiziert
res.WriteHeader(200) // HTTP 200
})
Die Folgenden Services sind Bestandteil von martini.Classic():
- *log.Logger - Globaler Logger für Martini.
- martini.Context - http request context.
- martini.Params -
map[string]string
von benannten Parametern, welche durch Route Matching gefunden wurden. - martini.Routes - Routen Hilfeservice.
- martini.Route - Aktuelle, aktive Route.
- http.ResponseWriter - http Response writer interface.
- *http.Request - http Request.
Eine Route ist in Martini eine HTTP-Methode gepaart mit einem URL-Matching-Pattern. Jede Route kann eine oder mehrere Handler-Methoden übernehmen:
m.Get("/", func() {
// zeige etwas an
})
m.Patch("/", func() {
// aktualisiere etwas
})
m.Post("/", func() {
// erstelle etwas
})
m.Put("/", func() {
// ersetze etwas
})
m.Delete("/", func() {
// lösche etwas
})
m.Options("/", func() {
// HTTP-Optionen
})
m.NotFound(func() {
// bearbeite 404-Fehler
})
Routen werden in der Reihenfolge, in welcher sie definiert wurden, zugeordnet. Die bei einer Anfrage zuerst zugeordnete Route wird daraufhin aufgerufen.
Routenmuster enthalten ggf. benannte Parameter, die über den martini.Params Service abrufbar sind:
m.Get("/hello/:name", func(params martini.Params) string {
return "Hallo " + params["name"]
})
Routen können mit Globs versehen werden:
m.Get("/hello/**", func(params martini.Params) string {
return "Hallo " + params["_1"]
})
Reguläre Ausdrücke sind ebenfalls möglich:
m.Get("/hello/(?P<name>[a-zA-Z]+)", func(params martini.Params) string {
return fmt.Sprintf ("Hallo %s", params["name"])
})
Weitere Informationen zum Syntax regulärer Ausdrücke findest Du in der Go Dokumentation.
Routen-Handler können auch ineinander verschachtelt werden. Dies ist bei der Authentifizierung und den Berechtigungen nützlich.
m.Get("/secret", authorize, func() {
// wird ausgeführt, solange authorize nichts zurückgibt
})
Routengruppen können durch die Group-Methode hinzugefügt werden.
m.Group("/books", func(r martini.Router) {
r.Get("/:id", GetBooks)
r.Post("/new", NewBook)
r.Put("/update/:id", UpdateBook)
r.Delete("/delete/:id", DeleteBook)
})
Sowohl Handlern als auch Middlewares können Gruppen übergeben werden.
m.Group("/books", func(r martini.Router) {
r.Get("/:id", GetBooks)
r.Post("/new", NewBook)
r.Put("/update/:id", UpdateBook)
r.Delete("/delete/:id", DeleteBook)
}, MyMiddleware1, MyMiddleware2)
Services sind Objekte, welche der Argumentliste von Handlern beigefügt werden können. Du kannst einem Service der Global oder Request Ebene zuordnen.
Eine Martini-Instanz implementiert das inject.Injector Interface, sodass ein Service leicht zugeordnet werden kann:
db := &MyDatabase{}
m := martini.Classic()
m.Map(db) // der Service ist allen Handlern unter *MyDatabase verfügbar
// ...
m.Run()
Das Zuordnen auf der Request-Ebene kann in einem Handler via martini.Context realisiert werden:
func MyCustomLoggerHandler(c martini.Context, req *http.Request) {
logger := &MyCustomLogger{req}
c.Map(logger) // zugeordnet als *MyCustomLogger
}
Einer der mächtigsten Aspekte von Services ist dessen Fähigkeit, einen Service einem Interface zuzuordnen. Möchtest Du den http.ResponseWriter mit einem Decorator (Objekt) und dessen Zusatzfunktionen überschreiben, definiere den Handler wie folgt:
func WrapResponseWriter(res http.ResponseWriter, c martini.Context) {
rw := NewSpecialResponseWriter(res)
c.MapTo(rw, (*http.ResponseWriter)(nil)) // überschribe ResponseWriter mit dem ResponseWriter Decorator
}
Eine martini.Classic() Instanz übertragt automatisch statische Dateien aus dem "public"-Ordner im Stammverzeichnis Deines Servers. Dieses Verhalten lässt sich durch weitere martini.Static Handler auf andere Verzeichnisse übertragen.
m.Use(martini.Static("assets")) // überträgt auch vom "assets"-Verzeichnis
Du kannst die URL zu einer lokalen Datei angeben, sollte die URL einer Anfrage nicht gefunden werden. Durch einen Präfix können bestimmte URLs ignoriert werden. Dies ist für Server nützlich, welche statische Dateien übertragen und ggf. zusätzliche Handler defineren (z.B. eine REST-API). Ist dies der Fall, so ist das Anlegen eines Handlers in der NotFound-Reihe nützlich.
Das gezeigte Beispiel zeigt die /index.html
immer an, wenn die angefrage URL keiner lokalen Datei zugeordnet werden kann bzw. wenn sie nicht mit /api/v
beginnt:
static := martini.Static("assets", martini.StaticOptions{Fallback: "/index.html", Exclude: "/api/v"})
m.NotFound(static, http.NotFound)
Middleware-Handler befinden sich logisch zwischen einer Anfrage via HTTP und dem Router. Im wesentlichen unterscheiden sie sich nicht von anderen Handlern in Martini. Du kannst einen Middleware-Handler dem Stack folgendermaßen anfügen:
m.Use(func() {
// durchlaufe die Middleware
})
Volle Kontrolle über den Middleware Stack erlangst Du mit der Handlers
-Funktion.
Sie ersetzt jeden zuvor definierten Handler:
m.Handlers(
Middleware1,
Middleware2,
Middleware3,
)
Middleware Handler arbeiten gut mit Aspekten wie Logging, Berechtigungen, Authentifizierung, Sessions, Komprimierung durch gzip, Fehlerseiten und anderen Operationen zusammen, die vor oder nach einer Anfrage passieren.
// überprüfe einen API-Schlüssel
m.Use(func(res http.ResponseWriter, req *http.Request) {
if req.Header.Get("X-API-KEY") != "secret123" {
res.WriteHeader(http.StatusUnauthorized)
}
})
Context.Next() ist eine optionale Funktion, die Middleware-Handler aufrufen können, um sie nach dem Beenden der anderen Handler auszuführen. Dies funktioniert besonders gut, wenn Operationen nach einer HTTP-Anfrage ausgeführt werden müssen.
// protokolliere vor und nach einer Anfrage
m.Use(func(c martini.Context, log *log.Logger){
log.Println("vor einer Anfrage")
c.Next()
log.Println("nach einer Anfrage")
})
Einige Martini-Handler machen von der globalen martini.Env
Variable gebrauch, die der Entwicklungsumgebung erweiterte Funktionen bietet, welche die Produktivumgebung nicht enthält. Es wird empfohlen, die MARTINI_ENV=production
Umgebungsvariable zu setzen, sobald der Martini-Server in den Live-Betrieb übergeht.
Starte die Suche mit einem Blick in die Projekte von martini-contrib. Solltest Du nicht fündig werden, kontaktiere ein Mitglied des martini-contrib Teams, um eine neue Repository anzulegen.
- acceptlang - Handler zum Parsen des
Accept-Language
HTTP-Header. - accessflags - Handler zur Ermöglichung von Zugriffskontrollen.
- auth - Handler zur Authentifizierung.
- binding - Handler zum Zuordnen/Validieren einer Anfrage zu einem Struct.
- cors - Handler für CORS-Support.
- csrf - CSRF-Schutz für Applikationen
- encoder - Enkodierungsservice zum Datenrendering in den verschiedensten Formaten.
- gzip - Handler zum Ermöglichen von gzip-Kompression bei HTTP-Anfragen.
- gorelic - NewRelic Middleware
- logstasher - Middlewaredie Logstashkompatibles JSON ausgibt
- method - Überschreibe eine HTTP-Method via Header oder Formularfelder.
- oauth2 - Handler der den Login mit OAuth 2.0 in Martinianwendungen ermöglicht. Google Sign-in, Facebook Connect und Github werden ebenfalls unterstützt.
- permissions2 - Handler zum Mitverfolgen von Benutzern, Loginstatus und Berechtigungen.
- render - Handler, der einen einfachen Service zum Rendern von JSON und HTML-Templates bereitstellt.
- secure - Implementation von Sicherheitsfunktionen
- sessions - Handler mit einem Session service.
- sessionauth - Handler zur einfachen Aufforderung eines Logins für Routes und zur Bearbeitung von Benutzerlogins in der Sitzung
- strict - Strikter Modus.
- strip - URL-Prefix Stripping.
- staticbin - Handler for serving static files from binary data
- throttle - Middleware zum Drosseln von HTTP-Anfragen.
- vauth - Handler zur Webhook-Authentifizierung (momentan nur GitHub und TravisCI)
- web - hoisie web.go's Kontext
Eine Martiniinstanz implementiert http.Handler
, sodass Subrouten in bestehenden Servern einfach genutzt werden können. Hier ist eine funktionierende Martinianwendungen für die Google App Engine:
package hello
import (
"net/http"
"github.com/go-martini/martini"
)
func init() {
m := martini.Classic()
m.Get("/", func() string {
return "Hallo Welt!"
})
http.Handle("/", m)
}
Martinis Run
Funktion sucht automatisch nach den PORT und HOST Umgebungsvariablen, um diese zu nutzen. Andernfalls ist localhost:3000 voreingestellt.
Für mehr Flexibilität über den Port und den Host nutze stattdessen die martini.RunOnAddr
Funktion.
m := martini.Classic()
// ...
log.Fatal(m.RunOnAddr(":8080"))
Gin und Fresh aktualisieren Martini-Apps live.
Martinis Maxime ist Minimalismus und sauberer Code. Die meisten Beiträge sollten sich in den Repositories der martini-contrib Gruppe wiederfinden. Beinhaltet Dein Beitrag Veränderungen am Kern von Martini, zögere nicht, einen Pull Request zu machen.
Inspiriert von Express und Sinatra
Martini wird leidenschaftlich von niemand Geringerem als dem Code Gangsta entwickelt