Last active
December 13, 2018 16:39
HTTP Request Contexts in Go — Examples — http://elithrar.github.io/article/map-string-interface/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"log" | |
"net/http" | |
"github.com/gocraft/web" | |
) | |
type Context struct { | |
CSRFToken string | |
User string | |
} | |
// Our middleware *and* handlers must be defined as methods on our context struct, | |
// or accept the type as their first argument. This ties handlers/middlewares to our | |
// particular application structure/design. | |
func (c *Context) CSRFMiddleware(w web.ResponseWriter, r *web.Request, next web.NextMiddlewareFunc) { | |
token, err := GenerateToken(r) | |
if err != nil { | |
http.Error(w, "No good!", http.StatusInternalServerError) | |
return | |
} | |
c.CSRFToken = token | |
next(w, r) | |
} | |
func (c *Context) ShowSignupForm(w web.ResponseWriter, r *web.Request) { | |
// No need to type assert it: we know the type. | |
// We can just use the value directly. | |
fmt.Fprintf(w, "Our token is %v", c.CSRFToken) | |
} | |
func main() { | |
router := web.New(Context{}).Middleware((*Context).CSRFMiddleware) | |
router.Get("/signup", (*Context).ShowSignupForm) | |
err := http.ListenAndServe(":8000", router) | |
if err != nil { | |
log.Fatal(err) | |
} | |
} | |
func GenerateToken(r *web.Request) (string, error) { | |
return "our-fake-token", nil | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"errors" | |
"fmt" | |
"net/http" | |
"github.com/zenazn/goji" | |
"github.com/zenazn/goji/web" | |
) | |
var ErrTypeNotPresent = errors.New("Expected type not present in the request context.") | |
// A little simpler: we just need this for every *type* we store. | |
func GetContextString(c web.C, key string) (string, error) { | |
val, ok := c.Env[key].(string) | |
if !ok { | |
return "", ErrTypeNotPresent | |
} | |
return val, nil | |
} | |
// A bare-bones example | |
func CSRFMiddleware(c *web.C, h http.Handler) http.Handler { | |
fn := func(w http.ResponseWriter, r *http.Request) { | |
maskedToken, err := GenerateToken(r) | |
if err != nil { | |
http.Error(w, "No good!", http.StatusInternalServerError) | |
return | |
} | |
// Goji only allocates a map when you ask for it. | |
if c.Env == nil { | |
c.Env = make(map[string]interface{}) | |
} | |
// Not a global - a reference to the context map | |
// is passed to our handlers explicitly. | |
c.Env["csrf_token"] = maskedToken | |
h.ServeHTTP(w, r) | |
} | |
return http.HandlerFunc(fn) | |
} | |
// Goji's web.HandlerFunc type is an extension of net/http's http.HandlerFunc, | |
// except it also passes in a request context (aka web.C.Env) | |
func ShowSignupForm(c web.C, w http.ResponseWriter, r *http.Request) { | |
// We'll use our helper function so we don't have to type assert | |
// the result in every handler. | |
csrfToken, err := GetContextString(c, "csrf_token") | |
if err != nil { | |
http.Error(w, "No good!", http.StatusInternalServerError) | |
return | |
} | |
// We can access this token in every handler we wrap with our | |
// middleware. No need to set/get from a session multiple times per | |
// request (which is slow!) | |
fmt.Fprintf(w, "Our token is %v", csrfToken) | |
} | |
func main() { | |
goji.Get("/signup", ShowSignupForm) | |
goji.Use(CSRFMiddleware) | |
goji.Serve() | |
} | |
func GenerateToken(r *http.Request) (string, error) { | |
return "our-fake-token", nil | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"errors" | |
"fmt" | |
"net/http" | |
"github.com/gorilla/context" | |
) | |
type contextKey int | |
// Define keys that support equality. | |
const csrfKey contextKey = 0 | |
const userKey contextKey = 1 | |
var ErrCSRFTokenNotPresent = errors.New("CSRF token not present in the request context.") | |
// We'll need a helper function like this for every key:type | |
// combination we store in our context map else we repeat this | |
// in every middleware/handler that needs to access the value. | |
func GetCSRFToken(r *http.Request) (string, error) { | |
val, ok := context.GetOk(r, csrfKey) | |
if !ok { | |
return "", ErrCSRFTokenNotPresent | |
} | |
token, ok := val.(string) | |
if !ok { | |
return "", ErrCSRFTokenNotPresent | |
} | |
return token, nil | |
} | |
// A bare-bones example | |
func CSRFMiddleware(h http.Handler) http.Handler { | |
fn := func(w http.ResponseWriter, r *http.Request) { | |
// A minimal example: in reality, our CSRF middleware would | |
// do a *lot* more than just this. | |
maskedToken, err := GenerateToken(r) | |
if err != nil { | |
http.Error(w, "No good!", http.StatusInternalServerError) | |
return | |
} | |
// The map is global, so we just call the Set function | |
context.Set(r, csrfKey, maskedToken) | |
h.ServeHTTP(w, r) | |
} | |
return http.HandlerFunc(fn) | |
} | |
func ShowSignupForm(w http.ResponseWriter, r *http.Request) { | |
// We'll use our helper function so we don't have to type assert | |
// the result in every handler that triggers/handles a POST request. | |
csrfToken, err := GetCSRFToken(r) | |
if err != nil { | |
http.Error(w, "No good!", http.StatusInternalServerError) | |
return | |
} | |
// We can access this token in every handler we wrap with our | |
// middleware. No need to set/get from a session multiple times per | |
// request (which is slow!) | |
fmt.Fprintf(w, "Our token is %v", csrfToken) | |
} | |
func main() { | |
r := http.NewServeMux() | |
r.Handle("/signup", CSRFMiddleware(http.HandlerFunc(ShowSignupForm))) | |
// Critical that we call context.ClearHandler here, else we leave | |
// old requests in the map. | |
err := http.ListenAndServe(":8000", context.ClearHandler(r)) | |
if err != nil { | |
log.Fatal(err) | |
} | |
} | |
func GenerateToken(r *http.Request) (string, error) { | |
return "our-fake-token", nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You have not imported "log" in gorilla example: