Skip to content

Instantly share code, notes, and snippets.

@omeid
Last active August 29, 2015 14:17
Show Gist options
  • Save omeid/9ec918cb557ce2e71296 to your computer and use it in GitHub Desktop.
Save omeid/9ec918cb557ce2e71296 to your computer and use it in GitHub Desktop.
Go API middleware handler.
package api
import (
"net/http"
"github.com/ORG/PROJECT/user"
)
func accessControl(level user.AccessLevel) func(handler) handler {
return func(handler handler) handler {
return func(w http.ResponseWriter, r *http.Request, ctx *Context) {
if ctx.User == nil {
APIError(w, http.StatusUnauthorized)
return
}
if ctx.User.AccessLevel < level {
//Error, no access.
APIError(w, http.StatusNotFound)
return
}
handler(w, r, ctx)
}
}
}
package api
import (
"log"
"net/http"
"time"
"github.com/gorilla/handlers"
"github.com/rs/cors"
"github.com/unrolled/secure"
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
"github.com/ORG/PROJECT/user"
)
func responseTime(next handler) handler {
return func(rw http.ResponseWriter, r *http.Request, ctx *Context) {
start := time.Now()
log.Printf("Started %s %s", r.Method, r.URL.Path)
next(rw, r, ctx)
log.Printf("Completed %s %s in %v\n\n", r.Method, r.URL.Path, time.Since(start))
}
}
func Register(r *mux.Router) {
common := Chain(
responseTime,
metrics,
alice(handlers.CompressHandler),
alice(secure.New(Options.Secure).Handler),
)
r.Methods("GET").Path("/ping").Handler(common.Then(pong))
r.Methods("POST").Path("/signup").Handler(common.Then(signup))
r.Methods("POST").Path("/login").Handler(common.Then(login))
//User only.
member := common.With(accessControl(user.Member))
r.Methods("POST").Path("/posts").Handler(member.Then(newpost))
r.Methods("GET").Path("/drafts").Handler(member.Then(drafts))
//God only can touch this!
god := common.With(accessControl(user.God))
r.Methods("GET", "HEAD").Path("/users").Handler(god.Then(users))
}
package api
//Alice was here.
import (
"log"
"net/http"
"github.com/golang/gddo/httputil" //TODO: vendor this thing.
"github.com/ORG/PROJECT/user"
)
type handler func(http.ResponseWriter, *http.Request, *Context)
type constructor func(next handler) handler
func Chain(c constructor, cs ...constructor) chain {
return append([]constructor{c}, cs...)
}
type chain []constructor
func (ch chain) With(c constructor, cs ...constructor) chain {
return append(append(ch, c), cs...)
}
func (ch chain) Then(final handler) http.Handler {
for i := len(ch) - 1; i >= 0; i-- {
final = ch[i](final)
}
return App(final)
}
type App handler
const ct = "application/json"
func (app App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if httputil.NegotiateContentType(r, []string{ct}, "") != ct {
http.Error(w, http.StatusText(http.StatusNotAcceptable), http.StatusNotAcceptable)
return
}
w.Header().Add("Content-Type", ct)
ctx, err := NewContext(w, r)
if err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
log.Println(err)
return
}
if vid, ok := ctx.Store.Get("user"); ok {
var err error
if id, ok := vid.(int64); ok {
ctx.User, err = user.Id(id)
}
if err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
log.Println("Invalid user-type in session.")
return
}
}
app(w, r, ctx)
}
func alice(base func(http.Handler) http.Handler) constructor {
return func(next handler) handler {
return func(w http.ResponseWriter, r *http.Request, c *Context) {
base(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next(w, r, c)
})).ServeHTTP(w, r)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment