Martini is a powerful package for quickly writing modular web applications/services in Golang.
package main
import "github.com/codegangsta/martini"
func main() {
m := martini.Classic()
m.Get("/", func() string {
return "Hello world!"
})
m.Run()
}
Install the package:
go get github.com/codegangsta/martini
- Extremely simple to use.
- Non-intrusive design.
- Play nice with other Golang packages.
- Awesome path matching and routing.
- Modular design - Easy to add functionality, easy to rip stuff out.
- Lots of good handlers/middlewares to use.
- Great 'out of the box' feature set.
- Fully compatible the http.HandlerFunc interface.
To get up and running quickly, martini.Classic() provides some reasonable defaults that work well for most web applications:
m := martini.Classic()
// ... middleware and routing goes here
m.Run()
Below is some of the functionality martini.Classic() pulls in automatically:
- Request/Response Logging - martini.Logger
- Panic Recovery - martini.Recovery
- Static File serving - martini.Static
- Routing - martini.Router
Handlers are the heart and soul of Martini. A handler is basically any kind of callable function:
m.Get("/", func() {
println("hello world")
}
If a handler returns something, Martini will write the result to the current *http.Request as a string:
m.Get("/", func() string {
return "hello world" // HTTP 200 : "hello world"
})
Handlers are invoked via reflection. Martini makes use of Dependency Injection to resolve dependencies in a Handlers argument list. This makes Martini completely compatible with golang's http.HandlerFunc
interface.
If you add an argument to your Handler, Martini will search it's list of services and attempt to resolve the dependency via type assertion:
m.Get("/", func(res http.ResponseWriter, req *http.Request) { // res and req are injected by Martini
res.WriteHead(200) // HTTP 200
})
The following services are included with martini.Classic():
- *log.Logger - Global logger for Martini.
- martini.Context - http request context.
- martini.Params -
map[string]string
of named params found by route matching. - http.ResponseWriter - http Response writer interface.
- *http.Request - http Request.
In Martini, a route is an HTTP method paired with a URL-matching pattern. Each route can take one or more handler methods:
m.Get("/", func() {
// show something
}
m.Post("/", func() {
// create something
}
m.Put("/", func() {
// replace something
}
m.Delete("/", func() {
// destroy something
}
Routes are matched in the order they are defined. The first route that matches the request is invoked.
Route patterns may include named parameters, accessible via the martini.Params service:
m.Get("/hello/:name", func(params martini.Params) string {
return "Hello " + params["name"]
})
Route handlers can be stacked on top of each other, which is useful for things like authentication and authorization:
m.Get("/secret", authorize, func() {
// this will execute as long as authorize doesn't write a response
})
Services are objects that are available to be injected into a Handler's argument list. You can map a service on a Global or Request level.
A Martini instance implements the inject.Injector interface, so mapping a service is easy:
db := &MyDatabase{}
m := martini.Classic()
m.Map(db) // the service will be available to all handlers as *MyDatabase
// ...
m.Run()
Mapping on the request level can be done in a handler via martini.Context:
func MyCustomLoggerHandler(c martini.Context, req *http.Request) {
logger := &MyCustomLogger{req}
c.Map(logger) // mapped as *MyCustomLogger
}
One of the most powerful parts about services is the ability to map a service to an interface. For instance, if you wanted to override the http.ResponseWriter with an object that wrapped it and performed extra operations, you can write the following handler:
func WrapResponseWriter(res http.ResponseWriter, c martini.Context) {
rw := NewSpecialResponseWriter(res)
c.MapTo(rw, (*http.ResponseWriter)(nil)) // override ResponseWriter with our wrapper ResponseWriter
}
A martini.Classic() instance automatically serves static files from the "public" directory in the root of your server. You can serve from more directories by adding more martini.Static handlers.
m.Use(martini.Static("assets")) // serve from the "assets" directory as well
Middleware Handlers sit between the incoming http request and the router. In essence they are no different than any other Handler in Martini. You can add a middleware handler to the stack like so:
m.Use(func() {
// do some middleware stuff
})
Middleware Handlers work really well for things like logging, authorization, authentication, sessions, gzipping, error pages and any other operations that must happen before or after an http request:
// validate an api key
m.Use(func(res http.ResponseWriter, req *http.Request) {
if req.Header.Get("X-API-KEY") != "secret123" {
res.WriteHeader(http.StatusUnauthorized)
}
})
Context.Next() is an optional function that Middleware Handlers can call to yield the until after the other Handlers have been executed. This works really well for any operations that must happen after an http request:
// log before and after a request
m.Use(func(c martini.Context, log *log.Logger){
log.Println("before a request")
c.Next()
log.Println("after a request")
})
Martini is obsessively designed by none other than the Code Gangsta