My first stackoverflow question, so please go easy on my naivety about stackoverflow and the question asked, beginner in golang.
I would like to know the difference between the two calls and also simple understanding of the Handle, Handler, HandleFunc, HandlerFunc.
http.Handle("/profile", Logger(profilefunc))
http.HandleFunc("/", HomeFunc)
func main() {
fmt.Println("Starting the server.")
profilefunc := http.HandlerFunc(ProfileFunc)
http.Handle("/profile", Logger(profilefunc))
http.HandleFunc("/", HomeFunc)
http.ListenAndServe("0.0.0.0:8081", nil)
}
func Logger(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
log.Println("Before serving request")
h.ServeHTTP(w, r)
log.Println("After serving request")
})
}
func ProfileFunc(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "You are on the profile page.")
}
func HomeFunc(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello Imran Pochi")
}
I would like ... simple understanding of the Handle, Handler, HandleFunc, HandlerFunc.
Handler is an interface that can respond to an HTTP request and has a ServeHTTP(ResponseWriter, *Request) method.
http.Handle registers a Handler to handle HTTP requests matching a given pattern.
http.HandleFunc registers a handler function to handle HTTP requests matching a given pattern. The handler function should be of the form func(ResponseWriter, *Request).
HandlerFunc is an explicit function type of the form func(ResponseWriter, *Request). HandlerFunc has a method ServeHTTP that calls itself . This allows you to cast a function to a HandlerFunc and use it as a Handler.
I would to know the difference between the two calls
http.Handle("/profile", Logger(profilefunc))
http.HandleFunc("/", HomeFunc)
Logger is an example of a middleware, which is a function that takes an http.Handler and it returns another http.Handler that wraps the original handler. When called this handler may (or may not) call the nested http.Handler before and/or after performing some operation. So the first line is saying register the profileFunc Handler wrapped in the Logger middleware with the pattern "/profile". The second line is saying register the HomeFunc function with the "/" pattern.
According to what I've seen from the Go documentation examples:
A Handler is a type created to respond to an HTTP request. To make a type a Handler all one has to do is implement a ServeHTTP() method. The ServeHTTP() method does the actual request processing.
Handle() takes the route and a Handler, the type that has an instance method named ServeHttp(). Note that it just takes the type, there's no need for one to point at the actual method/function that handles the request explicitly.
HandlerFunc is a type that internally implements a ServeHTTP() method. HandlerFunc is used to cast any Go function, with the right signature, to a HandlerFunc. The newly minted HandlerFunc is then passed to the Handle() method together with the route it processes.
Note that the HandlerFunc lets one implement request handlers by just writing functions without the need for a dedicated handler type.
HandleFunc() takes a route and any function that has the right signature. HandleFunc is shorthand for first of all doing the type casting and then passing the function to the Handle() method.
The signature for a request handling function is:
func handlerName(wr http.ResponseWriter, req *http.Request)
Related
What is the difference between chi.Use and chi.With when setting up a middleware with Chi router.
Use must be declared before all routes under the same group, whereas r.With allows you to "inline" middlewares.
As a matter of fact, the function signatures are different. Use returns nothing, With returns a chi.Router.
Let's say you have a route and want to add a middleware only to one of them, you would use r.With:
r.Route("/myroute", func(r chi.Router) {
r.Use(someMiddleware) // can declare it here
r.Get("/bar", handlerBar)
r.Put("/baz", handlerBaz)
// r.Use(someMiddleware) // can NOT declare it here
}
r.Route("/other-route", func(r chi.Router) {
r.Get("/alpha", handlerBar)
r.Put("/beta", handlerBaz)
r.With(someMiddleware).Get("/gamma", handlerQuux)
}
In the first example, someMiddleware is declared for all sub-routes, whereas in the second example r.With allows you to add a middleware only for the /other-route/gamma route.
According to the documentation of chi.Use and chi.With.
Use appends a middleware handler to the Mux middleware stack.
The middleware stack for any Mux will execute before searching for a matching route to a specific handler, which provides opportunity to respond early, change the course of the request execution, or set request-scoped values for the next http.Handler.
With adds inline middlewares for an endpoint handler.
Let see how chi.Use and chi.With example
The use case is pretty straight forward with chi.Use the registered middleware will run before all the routes handler which are register with the Router
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("welcome"))
})
http.ListenAndServe(":3000", r)
For eg: here Logger middleware will be called before all the register routes handler.
whereas with chi.With you are returned new route on which the middleware would be ran so if any routes is registered on the returned Router the registered middleware will run. Here the use case is very specific suppose if you want to run a specific middleware for a group of routes or want to perform some operation for specific routes then for the case you can use chi.Use
r.Route("/articles", func(r chi.Router) {
r.With(paginate).Get("/", listArticles) // GET /articles
r.With(paginate).Get("/{month}-{day}-{year}", listArticlesByDate) // GET /articles/01-16-2017
r.Post("/", createArticle) // POST /articles
r.Get("/search", searchArticles) // GET /articles/search
// Regexp url parameters:
r.Get("/{articleSlug:[a-z-]+}", getArticleBySlug) // GET /articles/home-is-toronto
// Subrouters:
r.Route("/{articleID}", func(r chi.Router) {
r.Use(ArticleCtx)
r.Get("/", getArticle) // GET /articles/123
r.Put("/", updateArticle) // PUT /articles/123
r.Delete("/", deleteArticle) // DELETE /articles/123
})
})
In the above example the paginate middleware will only be called for all the articles with /articles/ and /{month}-{day}-{year} day wise route for other routes chi.With won't be called if there any middlware registered with chi.Use over main route then that would be called.
This might just be because of my inexperience, but I've taken over a webserver solution written in Go and I have some issues.
The routing is set up in such a way that each "top" route is mounted to the router with router.Mount() with a handler attached to each mounted route. Example:
router.Mount("/group", (handler.NewGroupHandler(groupSrv, render)).Router())
So, within this mounted route I want to add a middleware to get a specific URL parameter. I've tried around, and followed this guide https://medium.com/#szablowska.patrycja/chi-and-missing-urlparam-in-middleware-9435c48a063b which seems to have worked for other. However, this haven't worked for me, which I now suspect is because of the approach with mounting.
The idea of the middleware is quite simple, just check the URL param against some requirements. I could of course do this manually in every path, but a middleware would be better both for readability and future development. How the middleware is written:
func GroupMiddleware() func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
groupIDStr := chi.URLParam(r, "group_id")
if checkAgainstGroupID {
// Do something, raise error
} else {
next.ServeHTTP(w, r)
}
}
return http.HandlerFunc(fn)
}
}
And then applied like this:
r.With(subhandler.GroupMiddleware).Route("/{group_id}", func(r chi.Router) {
r.Get("/", h.get)
})
Which obviously doesn't work. Any help would be appreciated because I can't believe that mounting routes makes it impossible to add middleware to subgroups.
Edit: This worked perfectly, it was just me that didn't register that I called an unaffected route. So in case someone else should end up with this same question, know that this actually is a valid approach.
I have an issue to implement middleware i want to use negroni.Wrap function for validate user location and one for calling handler below is my route:
r.Handle("/users/{userID}", negroni.New(
negroni.HandlerFunc(validateTokenMiddleware),
negroni.Wrap(&userLocation),
negroni.Wrap(&userDetailHandler),
)).Methods("GET")
and &userLocation is object of an struct which contain db information, here when i request for handler then that time both wrap execute together. But i want execute &userlocation first and if any errors occur so next wrap should not be execute , how to resolve this issue.
Thanks.
Use a middleware having ServeHttp Method which will be called using userDetailHandler as receiver inside middleware. And pass your db struct in middleware as an argument . You should use single wrap.
r.Handle("/users/{userID}", negroni.New(
negroni.HandlerFunc(validateTokenMiddleware),
negroni.Wrap(userDetailHandler.Middleware(&userLocation)),
)).Methods("GET")
func (userDetailHandler *UserDetailHandler)Middleware(res http.ResponseWriter, req *http.Request, userLocation *userLocation){
// use your serve Http inside this
userDetailHandler.ServeHttp(res, req)
}
I'm using gorilla/context in a web app. The example in the docs looks like:
func MyHandler(w http.ResponseWriter, r *http.Request) {
//...
val := context.Get(r, foo.MyKey)
//...
}
How can I unit test a handler that works like this? The only way I've managed so far is to use the context package inside my tests. I'm thinking at the moment that I could inject a context parameter into the handler but then I'm not conforming to the HandlerFunc interface.
This is a classic cross-cutting concerns example.
You are using a 3rd party to magically handle input params for your unit under test. By that very definition, you are going to have to do some extra setup to prep the context for the state you want.
When it comes to Go http handlers (which the convention is to KISS it), you shouldn't need to "reach out of context" of your func to get extra data: keep all the data you need within your single func.
Personally, I try to avoid corrupting my handlers like this. I think I've only used gorilla's context once out of the dozens of big sites I've built. And that was basically to get around a cached response, just to keep the data refesh to the end user. Of which I simply ignored in my unit tests, as it was out-of-scrope of what I was testing.
Instead, I use middle-ware wrappers to setup the basics I want in my handles and modify the handler's signature accordingly.
caching
logging
authentication and authorization
json marshaling
context (e.g. expected User{} object loaded from DB)
...etc. I would create a middle-ware that wraps your handler when you register it with mux that uses gorilla/context to lookup your cookie or userid or something, hidrates that user object from cache, DB, redis, etc, and then calls your handler that would have a signature like:
func MyHandler(u User, p Page, w http.ResponseWriter, r *http.Request) {
// u is the User object loaded from middle-ware
// p is your global Page object... etc
}
That way, your unit tests inject only the items you need under test.
And you can integration test your middle-ware on a QA server with expected User and Page objects in a datastore.
The way my team does it is to add a name to the route handler and then in the tests we call that route by name.
This is how to add a route:
r.HandleFunc("/<route>", MyHandler).Methods("GET").Name("MyHandlerByName")
Then this is how to test it
r.Get("MyHandlerByName")
One way to test handles is modify the way in which they are created. for example, Creating a function that return a http.HandlerFunc, this function can have parameters. You can mock the values that you send to the function
Without parameters
func State() http.HandlerFunc {
return http.HandlerFunc(func(pResponse http.ResponseWriter, r *http.Request) {
// your code
})
}
With Parameters
func State(pParam1,pParam2,pParam3 ...) http.HandlerFunc {
return http.HandlerFunc(func(pResponse http.ResponseWriter, r *http.Request) {
// your code using pParam1,pParam2,pParam3
})
}
The mapping will be
http.HandleFunc("/State", State())
or
http.HandleFunc("/State", State(value1,value2,value3 ....))
Given a middleware function with a signature like:
func Middleware(handler http.Handler) http.Handler
How can I get response headers set and passed to it from a calling function?
First a HandlerFunc (the "base" or main server function) is initialized with some response headers. Then it is passed to and wrapped by the middleware function in the conventional manner.
How can I get the Response and its headers from the handler parameter?
I've tried a number of things but can't get at the header values.
Strictly speaking, you cannot. In priniple, the http.Handler is a function that is invoked for each request. As such, it is not bound to any specific request; it will be re-used for an arbitrary number of requests. As such, there is no way to retrieve HTTP request or response headers from an existing http.Handler object.
You can however, decorate the handler with your own middleware to access HTTP response headers that were set by the middleware function.
A middleware function as described in your question will typically be implemented like this:
func Middleware(originalHandler http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
// call the original handler that is wrapped by this middleware
originalHandler.ServeHTTP(rw, req)
}
}
This function "decorates" the original http.Handler that is passed as a parameter into the middleware function by returning a new handler that implements custom logic and calls the original (wrapped) handler function at some point.
Within the new function, you can access the http.ResponseWriter object as usual to access the HTTP headers that were already set by the decorated handlers:
func Middleware(originalHandler http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
// Add new headers to the response...
rw.Header().Set("X-My-Own-Header", "foo")
// ...or access data from the original request
fmt.Println(req.Header().Get("X-My-Request-Header")
// call the original handler that is wrapped by this middleware
originalHandler.ServeHTTP(rw, req)
// Access headers in the response object
rw.Header().Get("X-Header-Set-By-Middleware")
}
}