-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add middleware support as discussed in #293 #294
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -238,5 +238,70 @@ as well: | |
url, err := r.Get("article").URL("subdomain", "news", | ||
"category", "technology", | ||
"id", "42") | ||
|
||
Since **vX.Y.Z**, mux supports the addition of middlewares to a [Router](https://godoc.org/github.com/gorilla/mux#Router), which are executed if a | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update to align with the README (Note to self: use tooling to make this automatic...) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I removed the version thingie. I'm not sure if its an important enough information to be in the readme. Feel free to re-add it if you think it is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good move. I think it's fair to assume that the documentation reflects There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yikes, I totally missed that one :/ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’ll take a look today. The website doesn’t do Markdown, which is a bit of a pain. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cool, thanks guys :) |
||
match is found (including subrouters). Middlewares are defined using the de facto standard type: | ||
|
||
type MiddlewareFunc func(http.Handler) http.Handler | ||
|
||
Typically, the returned handler is a closure which does something with the http.ResponseWriter and http.Request passed to it, and then calls the handler passed as parameter to the MiddlewareFunc (closures can access variables from the context where they are created). | ||
|
||
A very basic middleware which logs the URI of the request being handled could be written as: | ||
|
||
func simpleMw(next http.Handler) http.Handler { | ||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
// Do stuff here | ||
log.Println(r.RequestURI) | ||
// Call the next handler, which can be another middleware in the chain, or the final handler. | ||
next.ServeHTTP(w, r) | ||
}) | ||
} | ||
|
||
Middlewares can be added to a router using `Router.Use()`: | ||
|
||
r := mux.NewRouter() | ||
r.HandleFunc("/", handler) | ||
r.AddMiddleware(simpleMw) | ||
|
||
A more complex authentication middleware, which maps session token to users, could be written as: | ||
|
||
// Define our struct | ||
type authenticationMiddleware struct { | ||
tokenUsers map[string]string | ||
} | ||
|
||
// Initialize it somewhere | ||
func (amw *authenticationMiddleware) Populate() { | ||
amw.tokenUsers["00000000"] = "user0" | ||
amw.tokenUsers["aaaaaaaa"] = "userA" | ||
amw.tokenUsers["05f717e5"] = "randomUser" | ||
amw.tokenUsers["deadbeef"] = "user0" | ||
} | ||
|
||
// Middleware function, which will be called for each request | ||
func (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler { | ||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
token := r.Header.Get("X-Session-Token") | ||
|
||
if user, found := amw.tokenUsers[token]; found { | ||
// We found the token in our map | ||
log.Printf("Authenticated user %s\n", user) | ||
next.ServeHTTP(w, r) | ||
} else { | ||
http.Error(w, "Forbidden", 403) | ||
} | ||
}) | ||
} | ||
|
||
r := mux.NewRouter() | ||
r.HandleFunc("/", handler) | ||
|
||
amw := authenticationMiddleware{} | ||
amw.Populate() | ||
|
||
r.Use(amw.Middleware) | ||
|
||
Note: The handler chain will be stopped if your middleware doesn't call `next.ServeHTTP()` with the corresponding parameters. This can be used to abort a request if the middleware writer wants to. | ||
|
||
*/ | ||
package mux |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package mux | ||
|
||
import "net/http" | ||
|
||
// MiddlewareFunc is a function which receives an http.Handler and returns another http.Handler. | ||
// Typically, the returned handler is a closure which does something with the http.ResponseWriter and http.Request passed | ||
// to it, and then calls the handler passed as parameter to the MiddlewareFunc. | ||
type MiddlewareFunc func(http.Handler) http.Handler | ||
|
||
// middleware interface is anything which implements a MiddlewareFunc named Middleware. | ||
type middleware interface { | ||
Middleware(handler http.Handler) http.Handler | ||
} | ||
|
||
// MiddlewareFunc also implements the middleware interface. | ||
func (mw MiddlewareFunc) Middleware(handler http.Handler) http.Handler { | ||
return mw(handler) | ||
} | ||
|
||
// Use appends a MiddlewareFunc to the chain. Middleware can be used to intercept or otherwise modify requests and/or responses, and are executed in the order that they are applied to the Router. | ||
func (r *Router) Use(mwf MiddlewareFunc) { | ||
r.middlewares = append(r.middlewares, mwf) | ||
} | ||
|
||
// useInterface appends a middleware to the chain. Middleware can be used to intercept or otherwise modify requests and/or responses, and are executed in the order that they are applied to the Router. | ||
func (r *Router) useInterface(mw middleware) { | ||
r.middlewares = append(r.middlewares, mw) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e.g.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw, I forgot to mention I did not add the link because most handler don't actually implement
mux.Middleware
, as they require additional parameters. We could circunvent this using closures, but I think it would confuse some users instead of adding clarity. Maybe we could rework some handlers to make them comply with the interface?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe instead of changing them we could add a
XXXXMiddleware
function for each one?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, of course. We should not break existing handlers, so maybe wrapping them somehow, or refactoring the core logic into a different, private function and exposing both the actual and the Middleware flavours of it. I can give it a shot if you think it's worth it.