I am designing my handlers to return a http.Handler. Here's the design of my handlers:
func Handler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
})
}
My middleware is designed to accept an http.Handler and then call the handler once the middleware has finished performing its operations. Here's the design of my middleware:
func Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Middleware operations
next.ServeHTTP(w, r)
})
}
Considering the design of my middleware and handlers, what is the proper way of passing information from the middleware to the handler? The information that I am trying to pass from my middleware to the handlers is a JSON web token parsed from the request body. If I do not pass the parsed JWT to the handler, then I will need to parse the JWT again in my handlers. Parsing the request body for a JWT in both the middleware and handler seems wasteful. Just in case this information is relevant, I am using the standard net/http library with gorilla mux.
Since you're already using Gorilla take a look at the context package.
(This is nice if you don't want to change your method signatures.)
import (
"github.com/gorilla/context"
)
...
func Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Middleware operations
// Parse body/get token.
context.Set(r, "token", token)
next.ServeHTTP(w, r)
})
}
...
func Handler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := context.Get(r, "token")
})
}
Update
The Gorilla context package is now in maintenance mode
per the repo:
Note gorilla/context, having been born well before context.Context existed, does not play well with the shallow copying of the request that http.Request.WithContext (added to net/http Go 1.7 onwards) performs.
Using gorilla/context may lead to memory leaks under those conditions, as the pointers to each http.Request become "islanded" and will not be cleaned up when the response is sent.
You should use the http.Request.Context() feature in Go 1.7.
The proper way to pass request scoped data would now be the context package in the standard library.
https://golang.org/pkg/context/
You can access it with request.Context on an http.Request.
A first approach, similar to the question, is in codemodus/chain by Daved.
Package chain aids the composition of Handler wrapper chains that carry request-scoped data.
It uses the notion of Context, coupled with a Context handler:
func ctxHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
// ...
if s, ok := getMyString(ctx); ok {
// s = "Send this down the line."
}
// ...
}
Another approach: You can have a look at "Custom Handlers and Avoiding Globals in Go Web Applications", by Matt Silverlock (elithrar). (full example here)
The idea is to define ServeHTTP on a type which include the relevant context.
// We've turned our original appHandler into a struct with two fields:
// - A function type similar to our original handler type (but that now takes an *appContext)
// - An embedded field of type *appContext
type appHandler struct {
*appContext
h func(*appContext, http.ResponseWriter, *http.Request) (int, error)
}
// Our ServeHTTP method is mostly the same, and also has the ability to
// access our *appContext's fields (templates, loggers, etc.) as well.
func (ah appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Updated to pass ah.appContext as a parameter to our handler type.
status, err := ah.h(ah.appContext, w, r)
if err != nil {
log.Printf("HTTP %d: %q", status, err)
switch status {
case http.StatusNotFound:
http.NotFound(w, r)
// And if we wanted a friendlier error page, we can
// now leverage our context instance - e.g.
// err := ah.renderTemplate(w, "http_404.tmpl", nil)
case http.StatusInternalServerError:
http.Error(w, http.StatusText(status), status)
default:
http.Error(w, http.StatusText(status), status)
}
}
}
In the appContext struct, you would put any data you want to pass around.
Related
Hi
there is a need to pass a json object to check authentication in each request
for this purpose i am using gorilla package
func middlewareFirstAuthCheck(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("middlewareFirstAuthCheck - Before Handler")
next.ServeHTTP(w, r)
})
}
func StandartHandler(w http.ResponseWriter, r *http.Request) {
}
in standard handler i accept json object, and in middlewareFirstAuthCheck i accept different json object
middlewareFirstAuthCheck is executed first
there is an understanding of what needs to be done, but how to implement it correctly?
Keep in mind that the code running inside this function is not safe for a mux server:
func middlewareFirstAuthCheck(next http.Handler) http.Handler {
// don't manage something related to request if you have a mux server
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("middlewareFirstAuthCheck - Before Handler")
// you could get the r.Body or r.Headers here to get the user authenticated
next.ServeHTTP(w, r)
})
}
Ideally if you wanna handle permissions you should be setting them on the middleware depending the scope of your endpoint. Something like:
// please don't mind the terrible function/variable names
router.Get("/url", authMiddlewareForScope(s.handleTask(), "scope:to:handle:task"))
If you want this roles to be configurable by your application you will need a more elegant solution.
Please let me know if
Problem statement
I want to tie the lifetime of an HTTP request to a context that was created outside the scope of the web application. Thus, I wrote the following middleware (using github.com/go-chi/chi):
func BindContext(c context.Context) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h.ServeHTTP(w, r.WithContext(c))
})
}
}
The middleware is used in the following minimal test case:
package main
import (
"context"
"net/http"
"github.com/SentimensRG/ctx"
"github.com/SentimensRG/ctx/sigctx"
"github.com/go-chi/chi"
)
func greet(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
func BindContext(c context.Context) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h.ServeHTTP(w, r.WithContext(c))
})
}
}
func main() {
r := chi.NewMux()
r.Use(BindContext(ctx.AsContext(sigctx.New())))
r.Get("/", greet)
http.ListenAndServe(":8080", r)
}
The handler panics with the following error:
2018/07/25 14:58:57 http: panic serving [::1]:56527: interface conversion: interface {} is nil, not *chi.Context
goroutine 35 [running]:
net/http.(*conn).serve.func1(0xc42014a0a0)
/usr/local/go/src/net/http/server.go:1726 +0xd0
panic(0x12749c0, 0xc42014c200)
/usr/local/go/src/runtime/panic.go:502 +0x229
github.com/go-chi/chi.(*Mux).routeHTTP(0xc4201180c0, 0x12fcf00, 0xc420166000, 0xc420160200)
/Users/lthibault/go/src/github.com/go-chi/chi/mux.go:400 +0x2f3
github.com/go-chi/chi.(*Mux).(github.com/go-chi/chi.routeHTTP)-fm(0x12fcf00, 0xc420166000, 0xc420160200)
/Users/lthibault/go/src/github.com/go-chi/chi/mux.go:368 +0x48
net/http.HandlerFunc.ServeHTTP(0xc420142010, 0x12fcf00, 0xc420166000, 0xc420160200)
/usr/local/go/src/net/http/server.go:1947 +0x44
main.fail.func1.1(0x12fcf00, 0xc420166000, 0xc420160100)
/Users/lthibault/go/src/github.com/lthibault/mesh/cmd/scratch/main.go:22 +0x77
net/http.HandlerFunc.ServeHTTP(0xc420148000, 0x12fcf00, 0xc420166000, 0xc420160100)
/usr/local/go/src/net/http/server.go:1947 +0x44
github.com/go-chi/chi.(*Mux).ServeHTTP(0xc4201180c0, 0x12fcf00, 0xc420166000, 0xc420160000)
/Users/lthibault/go/src/github.com/go-chi/chi/mux.go:81 +0x221
net/http.serverHandler.ServeHTTP(0xc420150000, 0x12fcf00, 0xc420166000, 0xc420160000)
/usr/local/go/src/net/http/server.go:2694 +0xbc
net/http.(*conn).serve(0xc42014a0a0, 0x12fd1c0, 0xc42014c080)
/usr/local/go/src/net/http/server.go:1830 +0x651
created by net/http.(*Server).Serve
/usr/local/go/src/net/http/server.go:2795 +0x27b
Inelegant solution
The problem appears to come from Mux.routeHTTP, where an attempt is made to recover a *chi.Context from r.Context(). It would appear that r.WithContext does not transfer values stored in the request context to the new context.
The obvious (albeit ugly) fix is:
func BindContext(c context.Context) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rctx := r.Context().Value(chi.RouteCtxKey).(*chi.Context)
c = context.WithValue(c, chi.RouteCtxKey, rctx)
h.ServeHTTP(w, r.WithContext(c))
})
}
}
This works, but leaves me feeling uneasy. Do I really need to manually transfer each relevant value from r.Context() into the context being passed to r.WithContext()?
There are several failure cases, here:
What happens when there are many different values to be transferred?
What happens when the context keys aren't exported (as is recommended in Go)?
What happens if the original context terminates before the one I passed in?
(In few words: nothing good!)
My question is as follows
Is there a standard "merge" a context passed to r.WithContext with the exiting context in r.Context?
You should not replace the context on incoming request with an unrelated context. For starters:
Package context defines the Context type, which carries deadlines, cancelation signals, and other request-scoped values across API boundaries and between processes.
sigctx.New() is called before any request happens and is therefore by definition not request scoped. Pretty much all code expects the request context to be canceled when a) the request finishes, or b) the client aborts the request (usually because it is no longer interested in the response). You are breaking that assumption by replacing the context. You are also removing any values that other middlewares may have added to the context earlier.
It seems like you wish to abort requests on SIGINT or SIGTERM. You should add that cancelation condition to the request context instead of replacing it completely. Perhaps like so:
func BindContext(c context.Context) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rCtx := r.Context()
ctx, cancel := context.WithCancel(rCtx)
go func() {
select {
case <-c.Done(): // SIGINT/SIGTERM
case <-rCtx.Done(): // Request finished or client aborted
}
cancel()
}()
h.ServeHTTP(w, r.WithContext(ctx))
})
}
}
Update:
To let users configure the context, accept a function that derives a new context from the request context (although users might as well supply a middleware that does this directly):
func WithContext(new func(context.Context) context.Context) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r = r.WithContext(new(r.Context()))
h.ServeHTTP(w, r)
})
}
}
It appears as though there is no out-of-the-box solution for this, but github.com/SentimensRG/ctx provides a mergectx subpackage specifically for this purpose.
The solution is to use mergectx.Merge.
I faced the same problem and was able to resolve this by creating the new context using chi.NewRouteContext.
The request is being made using httptest. You can update the request its context using r.WithContext.
Example
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/", nil)
rctx := chi.NewRouteContext()
rctx.URLParams.Add("key", "value")
r = r.WithContext(context.WithValue(r.Context(), chi.RouteCtxKey, rctx))
handler := func(w http.ResponseWriter, r *http.Request) {
key := chi.URLParam(r, "key") // "value"
}
handler(w, r)
See the folling Gist from aapolkovsky: https://gist.github.com/aapolkovsky/1375348cab941e36c62da24a32fbebe7
I saw some http handler function declarations are varied.
Two of them I found are the standard function and the one returning anonymous function inside the handler.
For example:
Using standard way:
func helloworld(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello World")
}
This the most straight way to declare a handler for an http api.
Another way is using anonym/closure function inside the handler function:
func helloworld2() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
fmt.Fprintln(w, "Hello World")
})
}
What are the differences and the benefit? When to use one of them? What's the best practice?
Pattern
func Middleware(next http.Handler) http.Handler{
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Do something
next.ServeHTTP(w, r)
})
}
often used to construct middleware chain like
http.Handle("/", middlewareOne(middlewareTwo(finalHandler)))
Returning an anonymous function is the only way to work with handlers that require additional arguments, by returning a closure. Example:
func fooHandler(db *someDatabase) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// do something with `db` variable
}
}
Otherwise, there's typically no practical difference between the approaches. One may choose to use the anonymous function universally for consistency.
One of the most popular source of information about structure returning anonymous functions is a blog post from Mat Ryer How I write HTTP services after eight years
I sure it will be good to provide some quotes from his article here:
... handler functions don’t actually handle the requests, they return a function that does. This gives us a closure environment in which our handler can operate:
func (s *server) handleSomething() http.HandlerFunc {
thing := prepareThing()
return func(w http.ResponseWriter, r *http.Request) {
// use thing
}
}
The prepareThing is called only once, so you can use it to do one-time
per-handler initialisation, and then use the thing in the handler.
Also,
If an endpoint has its own request and response types, usually they’re only useful for that particular handler. If that’s the case, you can define them inside the function.
func (s *server) handleSomething() http.HandlerFunc {
// you have these handy structs always visible to your handler and eyes
// and invisible to code that don't use them
type request struct {
Name string
}
type response struct {
Greeting string `json:"greeting"`
}
return func(w http.ResponseWriter, r *http.Request) {
// decode into request struct
// validate
// call business-logic
// encode response from business-logic into response struct
}
}
In practice, writing RESTy APIs you have handler named after resource, e.g. you have /maps resource and appropriate handler struct mapsHandler with injected dependencies (repositories, services containing some business-logic, loggers) into it. But sometimes you will also need to pass an additional dependency exclusively per one handle and suddenly realized that handler has strict signature, so you should wrap it. Then you have something like this
// RESTy routes for "maps" resource
router.Route("/maps", func(r chi.Router) {
adHocDependency := newAdHocDependency(options)
r.Post("/", mapsHandler.handleCreateMap(adHocDependency))
})
making your ad hoc dependency visible to your handler.
Hope it helps!
I am building a Go web application that supports various middleware functions when handling routing. I'm trying to stick to net/http as much as possible and was wondering how I might accomplish this without using middleware libraries like negroni.
Essentially what I would like to do is to be able to provide a slice of middleware functions, say one for logging, one for checking for a valid JWT, and then finally the handler to handle the request.
I am able to do this with negroni fairly simply by defining the following struct:
// Route ..
type Route struct {
Method string
Path string
Middleware []negroni.Handler
Handler http.HandlerFunc
}
and then defining a route like:
var commonRoutes = []Route{
{
Method: "GET",
Path: "/info",
Middleware: []negroni.Handler{negroni.HandlerFunc(middleware.CheckCache), negroni.HandlerFunc(middleware.Authenticated), negroni.NewLogger()},
Handler: handlers.APIInfo,
},
}
Finally when I boot up my server, I import the list of routes and register them like so:
for _, r := range routes {
handler := append(r.Middleware, negroni.Wrap(r.Handler))
router.Handle(r.Path, negroni.New(handler...)).Methods(r.Method)
}
And this works perfectly.
Any idea how I might be able to do this with just the standard net/http signature and way of defining middleware handlers that look like this:
http.Handle("/", middlewareOne(middlewareTwo(finalHandler)))
Thank you :)
func Auth(n http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("Start")
n.ServeHTTP(w, r)
log.Printf("End")
})
}
func processReq(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Success"))
}
func main() {
handler := http.HandlerFunc(processReq)
http.Handle("/",Auth(handler))
http.ListenAndServe(":8000", nil)
}
can be done using http.handler
Simple. You define each handler like so:
// So I don't have to type it over and over...
type HTTPHandler func(w http.ResponseWriter, r *http.Request)
func Handler1(next HTTPHandler) HTTPHandler {
return func(w http.ResponseWriter, r *http.Request){
// Do stuff
if next != nil {
next(w, r)
}
}
}
// Handler2 ... HandlerN defined in the same basic way.
// Chaining:
http.Handle("/", Handler1(Handler2(nil)))
Each handler takes the next handler and returns a closure that does whatever you want plus calling the next handler. If you need lots of these it may make sense to write a helper similar to this one:
func MakeHandler(worker, next HTTPHandler) HTTPHandler {
return func(w http.ResponseWriter, r *http.Request){
// Maybe have to worker return an error and do standard error
// handling here? Could simplify your code some depending on
// what you are doing.
worker(w, r)
if next != nil {
next(w, r)
}
}
}
I am designing my handlers to return a http.Handler. Here's the design of my handlers:
func Handler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
})
}
My middleware is designed to accept an http.Handler and then call the handler once the middleware has finished performing its operations. Here's the design of my middleware:
func Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Middleware operations
next.ServeHTTP(w, r)
})
}
Considering the design of my middleware and handlers, what is the proper way of passing information from the middleware to the handler? The information that I am trying to pass from my middleware to the handlers is a JSON web token parsed from the request body. If I do not pass the parsed JWT to the handler, then I will need to parse the JWT again in my handlers. Parsing the request body for a JWT in both the middleware and handler seems wasteful. Just in case this information is relevant, I am using the standard net/http library with gorilla mux.
Since you're already using Gorilla take a look at the context package.
(This is nice if you don't want to change your method signatures.)
import (
"github.com/gorilla/context"
)
...
func Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Middleware operations
// Parse body/get token.
context.Set(r, "token", token)
next.ServeHTTP(w, r)
})
}
...
func Handler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := context.Get(r, "token")
})
}
Update
The Gorilla context package is now in maintenance mode
per the repo:
Note gorilla/context, having been born well before context.Context existed, does not play well with the shallow copying of the request that http.Request.WithContext (added to net/http Go 1.7 onwards) performs.
Using gorilla/context may lead to memory leaks under those conditions, as the pointers to each http.Request become "islanded" and will not be cleaned up when the response is sent.
You should use the http.Request.Context() feature in Go 1.7.
The proper way to pass request scoped data would now be the context package in the standard library.
https://golang.org/pkg/context/
You can access it with request.Context on an http.Request.
A first approach, similar to the question, is in codemodus/chain by Daved.
Package chain aids the composition of Handler wrapper chains that carry request-scoped data.
It uses the notion of Context, coupled with a Context handler:
func ctxHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
// ...
if s, ok := getMyString(ctx); ok {
// s = "Send this down the line."
}
// ...
}
Another approach: You can have a look at "Custom Handlers and Avoiding Globals in Go Web Applications", by Matt Silverlock (elithrar). (full example here)
The idea is to define ServeHTTP on a type which include the relevant context.
// We've turned our original appHandler into a struct with two fields:
// - A function type similar to our original handler type (but that now takes an *appContext)
// - An embedded field of type *appContext
type appHandler struct {
*appContext
h func(*appContext, http.ResponseWriter, *http.Request) (int, error)
}
// Our ServeHTTP method is mostly the same, and also has the ability to
// access our *appContext's fields (templates, loggers, etc.) as well.
func (ah appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Updated to pass ah.appContext as a parameter to our handler type.
status, err := ah.h(ah.appContext, w, r)
if err != nil {
log.Printf("HTTP %d: %q", status, err)
switch status {
case http.StatusNotFound:
http.NotFound(w, r)
// And if we wanted a friendlier error page, we can
// now leverage our context instance - e.g.
// err := ah.renderTemplate(w, "http_404.tmpl", nil)
case http.StatusInternalServerError:
http.Error(w, http.StatusText(status), status)
default:
http.Error(w, http.StatusText(status), status)
}
}
}
In the appContext struct, you would put any data you want to pass around.