gomiddleware
Golang Middleware (net/http and httprouter+alice)
That's talk about Golang standard lib net/http and my favorate lib httprouter + alice.
Thanks for this blog, it really inspire me alot.
https://libraries.io/go/github.com%2Fmontanaflynn%2Fgo-middleware
Use Standard net/http
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func enableCORS(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
if r.Method == "OPTIONS" {
w.WriteHeader(200)
}
handler.ServeHTTP(w, r)
})
}
func logRequests(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
now := time.Now()
method := r.Method
path := r.URL.Path
fmt.Printf("%v %s %s\n", now.Format("Jan 2, 2006 at 3:04pm (MST)"), method, path)
handler.ServeHTTP(w, r)
})
}
func delay(handler http.Handler, length string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
timeout, err := time.ParseDuration(length)
if err != nil {
log.Println("Could not parse delay length, skipping delay.")
handler.ServeHTTP(w, r)
}
time.Sleep(timeout)
handler.ServeHTTP(w, r)
})
}
func helloWorld(w http.ResponseWriter, r *http.Request) {
fmt.Println("haha in world ha")
w.Write([]byte("Hello world!"))
}
func helloWorld1(str string) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("haha in world1 ha")
w.Write([]byte("Hello world!"))
})
}
func helloWorld2(str string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("haha in world2 ha")
w.Write([]byte("Hello world!"))
})
}
func main() {
//app := delay(logRequests(enableCORS(http.HandlerFunc(helloWorld))), "1s")
app := delay(logRequests(enableCORS(http.HandlerFunc(helloWorld1("5")))), "5s")
//app := helloWorld2("5")
http.ListenAndServe(":8080", app)
There are three types of calling helloWord(x) function.
with middleware but without passing paramteters, said helloWorld
app := delay(logRequests(enableCORS(http.HandlerFunc(helloWorld))), "1s")
with middleware and with passing paramteters, said hellowWorld1
app := delay(logRequests(enableCORS(http.HandlerFunc(helloWorld1("5")))), "5s")
without middleware with parameter, said helloWorld2
app := helloWorld2("5")
One can check the method related to its function call.
Httprouter and Alice
package main
import (
"encoding/json"
"fmt"
"github.com/coreos/etcd/client"
"github.com/gorilla/context"
"github.com/julienschmidt/httprouter"
"github.com/justinas/alice"
"github.com/pborman/uuid"
"io"
"net/http"
"os"
"time"
)
func tokenHandler(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
fmt.Println("into middle recover")
// put process here
token := r.Header.Get("Auth-Token")
fmt.Println(token)
if token == "powertoken" {
next.ServeHTTP(w, r)
}
err := getTokenExist(kAPI, token)
if err == nil {
next.ServeHTTP(w, r)
} else {
http.Error(w, http.StatusText(401), 401)
}
// error oocured
}
return http.HandlerFunc(fn)
}
func wrapHandler(h http.Handler) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
context.Set(r, "params", ps)
h.ServeHTTP(w, r)
}
}
func getInfoHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
//a := make(map[string]string)
a := map[string]interface{}{}
a["test"] = "haha"
jsonString, _ := json.Marshal(a)
//w.WriteHeader(200)
w.Write(jsonString)
}
func getInfoHandler1(str string) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("I am here with str:", str)
w.Header().Set("Content-Type", "application/json")
//a := make(map[string]string)
a := map[string]interface{}{}
a["test"] = "haha"
jsonString, _ := json.Marshal(a)
//w.WriteHeader(200)
w.Write(jsonString)
})
}
func ttHandler(aa string) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
fmt.Println("in tt handler", aa)
w.Header().Set("WWW-Authenticate", "Basic realm=Restricted")
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
}
}
func startWeb() {
commonHandlers := alice.New(loggingHandler, tokenHandler, middlewareGenerator("foo", "foo2"))
router := httprouter.New()
router.GET("/version", wrapHandler(commonHandlers.ThenFunc(getInfoHandler)))
router.GET("/version1", wrapHandler(commonHandlers.ThenFunc(getInfoHandler1("haha"))))
aa := "strrrrr"
router.GET("/tt", ttHandler(aa))
http.ListenAndServe(":8080", router)
}
func main() {
//println("start web")
go startWeb()
fmt.Println("start over")
var input string
fmt.Scanln(&input)
}
There are three points here.
How to pass parameters to alice middleware said middlewareGenerator
alice.New(loggingHandler, tokenHandler, middlewareGenerator("foo", "foo2"))
How to pass parameters to httprouter and alice chain said getInfoHandler1
router.GET("/version1", wrapHandler(commonHandlers.ThenFunc(getInfoHandler1("haha"))))
How to pass parameters to httprouter said ttHandler
router.GET("/tt", ttHandler(aa))
One can check the method related to its function call.
Conclustion
Now you have different way to deal with different situations.
Just copy and paste it.
Keep in mind, it's all about golang's wrap function and return value.