Skip to content

Instantly share code, notes, and snippets.

@elithrar
Last active December 13, 2018 16:39
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save elithrar/015e2a561eee0ca71a77 to your computer and use it in GitHub Desktop.
Save elithrar/015e2a561eee0ca71a77 to your computer and use it in GitHub Desktop.
HTTP Request Contexts in Go — Examples — http://elithrar.github.io/article/map-string-interface/
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
}
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
}
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
}
@Jitendrakry
Copy link

Jitendrakry commented Dec 23, 2016

You have not imported "log" in gorilla example:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment