I am writing a web application with Go to get better with it. My use case is pretty simple. I want to have a common function that will be executed for every request and will generate the navigation bar depending on the user status.
init method looks like (will also give you the idea of my implementation of handler methods):
func init() {
initDB()
gob.Register(user.User{})
r := mux.NewRouter()
r.HandleFunc("/", handleHome)
http.Handle("/", r)
}
I am using the following method to execute templates.
func executeTemplate(w http.ResponseWriter, name string, status int, data map[string]interface{}) error {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(status)
data["User"] = getUser(r)
return tpls[name].ExecuteTemplate(w, "base", data)
}
I am using Gorilla toolkit to store the session but as of my understanding, I need the http.Request instance every time to access the cookie store. Now I don't want to change the signature of executeTemplate method. Is there any way I can add a function to generate the navigation bar without changing signature of any of the existing methods?
What are some good ways to do it (even with changing the existing methods)?
Basic common approach to create middleware in Gorillatoolkit is to wrap top-level mux. Something like
func Middleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
//You do something here using request
h.ServeHTTP(w, r)
})
}
And then
r := mux.NewRouter()
r.HandleFunc("/", handleHome)
http.Handle("/", Middleware(r))
Related
I have a question regarding middlewares in various places:
I was reading about using middlewares with just stdlib to write request handlers as mentioned here: (https://www.alexedwards.net/blog/making-and-using-middleware)
func middlewareOne(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Our middleware logic goes here...
next.ServeHTTP(w, r)
})
}
mux := http.NewServeMux()
mux.Handle("/", middlewareOne(middlewareTwo(finalHandler)))
It makes sense to me that passing one handler inside another in this format executes the next middleware's ServeHTTP
I am using dgrouter for discord command routing and I found that I could use middlewares in there as well as defined in this example:
https://github.com/Necroforger/dgrouter/blob/master/examples/middleware/middleware.go#L40-L59
The method used to register middlewares is defined like so:
https://github.com/Necroforger/dgrouter/blob/master/exrouter/router_wrapper.go#L41-L49
My question is why is the middleware not called like how it is done in net/http and just the function name is passed:
router.Group(func(r *exrouter.Route) {
// Added routes inherit their parent category.
// I set the parent category here and it won't affect the
// Actual router, just this group
r.Cat("main")
// This authentication middleware applies only to this group
r.Use(Auth)
log.Printf("len(middleware) = %d\n", len(r.Middleware))
r.On("testauth", func(ctx *exrouter.Context) {
ctx.Reply("Hello " + ctx.Get("member").(*discordgo.Member).Nick + ", you have permission to use this command")
})
})
r.Use(Auth)
Is what i have difficulty understanding. Why is the function not being called?
I want to trace the whole project with Opencensus and Jaeger. I added HTTP trace in entry services and add stratspan in middleware surrounded whol my services and this two-span called and show on Jaeger. My problem is each service contain a lot of function and I want see a trace of all my functions but in this way not show overall service not shown each function. I don't like add per function add one stratspan. I use ctx context.Context entry all my function but not different!
There's really not many options for other than starting a span in each function you'd like to instrument:
func something(ctx context.Context) {
ctx, span := trace.StartSpan(ctx, "something")
defer span.End()
}
If your functions have a common call signature, or you can coalesce your function into a common call signature, you can write a wrapper. Examples of this can be seen in http "middleware".
Consider the http.Handler, you could write a decorator for your functions that handles the span lifecycle:
func WithTraced(handler http.Handler, opName string) http.Handler {
return func(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(ctx, opName)
defer span.End()
handler.ServeHTTP(w, r.WithContext(ctx))
}
}
A similar pattern could be applied by embedding structs.
Go has come with a new package called context and with recent versions (Go 1.7 I think) we should be able to use it in the same way as gorilla/context package:
http://www.gorillatoolkit.org/pkg/context
With gorilla context you can very easily set and get variables that are relevant to a request, it's handlers, and middlewares.
To set a value in gorilla context is really easy:
func handleFunc(w http.ResponseWriter, r *http.Request) {
context.Set(r, "foo", "bar")
}
To get the value we can do:
func handleFunc(w http.ResponseWriter, r *http.Request) {
val := context.Get(r, "foo")
}
I understand that we can use this in middlewares so that the next middleware can use variables that were set in previous middleware. I would like to be able to do this with Go context package.
I understand that to get a value is quite simple like this:
func handleFunc(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.Context().Value("foo"))
}
But I have no idea how to set the value. It was not very intuitive for me and I don't really understand how to do it.
See "Exploring the context package", using WithValue and the context associated to the Request:
Middleware
Middleware in Go refers to an http handler which wraps around a multiplexer. There are several 3rd party middleware solutions (such as negroni), but really the standard library supports a very similar pattern. The use of a Context in the request allows us to hold data in the request.
See the example code for invocation and definition.
func putClientIPIntoContext(r *http.Request) context.Context {
ci := r.RemoteAddr
fwd := r.Header.Get("X-Forwarded-For")
if fwd != "" {
ci = fwd
}
ctx := context.WithValue(r.Context(), ClientIPKey, ci)
return ctx
}
The Context can store request-scoped variables.
It’s useful when writing ‘middleware’, but it’s a little bit ‘anti-pattern’ — it’s a bit magical, because it’s not type-safe.
See more at "Pitfalls of context values and how to avoid or mitigate them in Go".
The example below only shows how you might use the authentication logic from above to verify that when a user is logged in when visiting any page with a path prefix of /dashboard/.
A similar approach could be used to verify that a user is an admin before allowing them access to any page with a path prefix of /admin/.
func requireUser(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user := lookupUser(r)
if user == nil {
// No user so redirect to login
http.Redirect(w, r, "/login", http.StatusFound)
return
}
ctx := context.WithValue(r.Context(), "user", user)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func main() {
dashboard := http.NewServeMux()
dashboard.HandleFunc("/dashboard/hi", printHi)
dashboard.HandleFunc("/dashboard/bye", printBye)
mux := http.NewServeMux()
// ALL routes that start with /dashboard/ require that a
// user is authenticated using the requireUser middleware
mux.Handle("/dashboard/", requireUser(dashboard))
mux.HandleFunc("/", home)
http.ListenAndServe(":3000", addRequestID(mux))
}
As kostix comments, use Context wisely, like Dave Cheney suggest in "Context is for cancelation"
I want to wrap func StripPrefix inside another function and do some client session checking before making a file server. All the sessions are stored in a MySQL database table for implementing multiple web server for one website.
func RegisterHandlers() {
http.Handle("/", fileHandler)
http.HandleFunc("/login", loginHandler)
}
func fileHandler() http.Handler {
//Check session in the database
return http.StripPrefix("/", http.FileServer(http.Dir("client/index")))
}
Error Message
The error message said that missing ServeHTTP method. I don't understand why since I just wrap it in another function and return exactly http.StripPrefix.
./index.go:9: cannot use fileHandler (type func() http.Handler) as type http.Handler in argument to http.Handle:
func() http.Handler does not implement http.Handler (missing ServeHTTP method)
http.Handle expects a function that takes an http.ResponseWriter and an *http.Request. Your file handler function does not take either of these arguments. Instead, you want to pass in the handler returned from a call to your fileHandler function. However, if you do this as-is, you will only be checking the credentials once instead of on every HTTP call, which is probably not what you wanted. Instead, you have to return a handler function that wraps both your session check, and a call to the file servers Handle method. You could even have it wrap a generic handler (similar to how StripPrefix works) so that you can reuse it to check the session for multiple different types of requests. Something like the following:
func RegisterHandlers() {
http.Handle("/", checkSession(http.StripPrefix("/", http.FileServer(http.Dir("client/index")))))
http.HandleFunc("/login", loginHandler)
}
func checkSession(h http.Handler) http.Handler {
return HandlerFunc(func(w ResponseWriter, r *Request) {
//Check session in the database
h.ServeHTTP(w, r)
})
}
I want to determine a simple and useful pattern for user authentication in a web app being written in golang.
I have come up with two patterns. First one is enabling the programmer to have his functions separate form the authentication logic, and has cleaner HandleFunc parts in main() that one can see only by loking main() to see what parts are under authentication control.
Second one is making programmer include a decision in every function deal with authentication required urls. An if statement checks by a authp() function defined else where.
Which one is better pattern for such necessity?
What are the better patterns for this job?
Is it even possible to pass a function to http.HandleFunc that has signature other than func urlFunc (ResponseWriter, *Request) bu like func urlFunc (successFunc, failFunc) or func urlFunc (ResponseWriter, *Request, successFunc, failFunc) as in authenticationGateKeeper function of First Way below, if not a suitable workaround for that?
//First Way
package main
func authGateKeeper(successFunc, failFunc) {
if (authp()) {
successFunc
} else {
failFunc
}
}
func authp() boolean {
//authentication logic, db query, or session check etc.
}
//usage in main
http.HandleFunc("/", authGateKeeper)
//Second Way; other alternative, in each function check pattern
func f(w, r) {
if (authp()) {
//function's processes
} else {
//the fail case function or processes
}
}
func authp() boolean {
//authentication logic, db query, or session check etc.
}
//usage in main
http.HandleFunc("/", f)
There are many ways to spin this, and it's arguable whether one is outright "better". I'd strongly suggest writing some middleware that wraps your routes and enforces the check, calling the wrapped handler only on success.
Note that I'm going to make a few assumptions here as you haven't told us how you're managing sessions (cookies? server-side?) and/or what kind of authorization you might need on top of authentication.
// Middleware - a function that sits in the 'middle' of your request processing.
func RequireAuth(h http.Handler) http.Handler) {
fn := func(w http.ResponseWriter, r *http.Request) {
// Assuming gorilla/sessions
session, err := store.Get("name", r)
if err != nil {
// Raise HTTP 500
return
}
// We'll assume you're storing the userID in the cookie|server session
// upon login elsewhere.
id := session.Values["userID"]
// Probably returns a *yourapp.User
user, err := db.GetUser(id)
if err != nil {
// Raise HTTP 500
return
}
if user == nil {
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
// Don't forget these 'naked' returns - if you miss one, your
// handler will keep processing beyond the error and result in
// unintended side effects
return
}
// Further checks here - i.e. checking user.Active == true, etc.
// The userID matches one in the DB, so let's proceed
h.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
// And in your router - assuming just vanilla net/http
http.Handle("/", RequireAuth(yourHandlerFunc))
http.Handle("/", RequireAuth(someOtherHandler))
// Note that using gorilla/mux or goji can help give you "subrouters" so you
// don't have to wrap every single route with your middleware (messy, error prone)
I'd also suggest some reading on Go middleware1 composition2 which will help you in the future.
If you want to call a custom error page, just write a handler - e.g. UnauthorizedHandler that satisfies http.Handler and just call UnauthorizedHandler.ServeHTTP(w, r) instead of http.Error along the way.