Is it possible to get the base path from the http.HandleFunc in the http.Request or http.ResponseWriter as a variable (first argument in http.HandleFunc) ?
http.HandleFunc("/the-base-path/", func(w http.ResponseWriter, r *http.Request){
// get "/the-base-path/" here as a variable
...
})
Short answer: no.
Longer answer.
The function that serves requests is
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
The function mux.Handler that looks up the handler for the request returns both the handler and the corresponding patter (aka "/the-base-path/")
But as you can see mux.ServeHTTP drops the pattern returned by mux.Handler. There is no direct way for the handler to identify the pattern that was matched against the request.
Related
Here is the complete example from my current reading material "Hands-On Restful Web Services With Go" from Packt.
func filterContentType(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("Currently in the check content type middleware")
// Filtering requests by MIME type
if r.Header.Get("Content-type") != "application/json" {
w.WriteHeader(http.StatusUnsupportedMediaType)
w.Write([]byte("415 - Unsupported Media Type. Please send JSON"))
return
}
handler.ServeHTTP(w, r)
})
}
func setServerTimeCookie(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Setting cookie to every API response
cookie := http.Cookie{Name: "ServerTimeUTC", Value: strconv.FormatInt(time.Now().Unix(), 10)}
http.SetCookie(w, &cookie)
log.Println("Currently in the set server time middleware")
handler.ServeHTTP(w, r)
})
}
func handle(w http.ResponseWriter, r *http.Request) {
// Check if method is POST
if r.Method == "POST" {
var tempCity city
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&tempCity)
if err != nil {
panic(err)
}
defer r.Body.Close()
// Your resource creation logic goes here. For now it is plain print to console
log.Printf("Got %s city with area of %d sq miles!\n", tempCity.Name, tempCity.Area)
// Tell everything is fine
w.WriteHeader(http.StatusOK)
w.Write([]byte("201 - Created"))
} else {
// Say method not allowed
w.WriteHeader(http.StatusMethodNotAllowed)
w.Write([]byte("405 - Method Not Allowed"))
}
}
func main() {
originalHandler := http.HandlerFunc(handle)
http.Handle("/city", filterContentType(setServerTimeCookie(originalHandler))) // !
http.ListenAndServe(":8000", nil)
}
This program simply consists of the main function and 3 other functions, their logic is arbitrary and just copied from my book's example.
At bottom, where I've commented with "!", filterContentType is using an argument that itself is a function (setServerTimeCookie), and it looks like it's being invoked with originalHandler as its argument.
However when this code is run, the order of execution is:
filterContentType 2. setServerTimeCookie 3. originalHandler
This is counterintuitive to what I understand about using functions as arguments. I assumed that setServerTimeCookie would be the first to execute but that's not the case; it's behaving like an uninvoked function.
This leads to my question, what is causing setServerTimeCookie to defer its execution despite the syntax suggesting it's being invoked as filterContentType's argument?
I attempted to simplify things for my own understanding:
func main() {
one(two(three))
}
func one(f func()) {
fmt.Println("ONE\n")
f()
}
func two(f func()) {
fmt.Println("TWO\n")
f()
}
func three(){
fmt.Println("THREE\n")
}
This code does not build, I'm left with the error:
two(three) used as value -which tells me that two is being invoked, unlike the book's example.
What's the difference and again, why doesn't the book's example invoke setServerTimeCookie first? My only assumption is that it has something to do with the implementation of http.HandlerFunc so maybe I should start there.
Any insight to fast-forward my understanding would be greatly appreciated.
This doesn't compile because two(three) does not return a value.
I assume you want to return a function closure in this case, so to fix:
func two(f func()) func() {
return func() {
fmt.Println("TWO\n")
f()
}
}
https://go.dev/play/p/vBrAO6nwy4X
Circling back to your question about setServerTimeCookie and it's use of return http.HandlerFunc(fn). Looking at the source for http.HandlerFunc reveals it's actually a type definition - and NOT a conventional function call. It's actual IMHO the most powerful and underrated four lines of code in the go standard library:
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
By creating this value of http.HandlerFunc, it's implicitly a http.Handler, since it provides the ServeHTTP method. This therefore allows this method to be called upon request - which is exactly what a webservice is designed to do: the underlying function f will be invoked when the handler is invoked.
Because in the expression one(two(three)) function two is not passed as function reference. Instead function two is called with the argument tree, which is not what function one expects
I have a requirement of informing user the allowed methods for specific endpoint. This information will be shown in case there is 405 response from server (I'm using gorilla/mux).
I've trying using custom handler by mux for 405, but I can't find any info in the Request object and ResponseWriter.
After reading the docs and SO, I can't find any. May I know if anyone has been doing same thing before?
Code is below. I only allow GET apparently.
router.HandleFunc("/users/{id}",).Methods(http.MethodGet)
In my handler for 405, the response header is empty apparently. There is no info on allowed methods for this endpoint.
func MethodNotAllowedHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logrus.Debugln("Header Writer: ", w.Header())
})
Thanks!
You can modify your MethodNotAllowed handler such below;
It simply walks through the routes and applies routematch function. If path is matched and method is not, it returns mux.ErrMethodMismatch error. Then you can obtain allowed methods for the route and send it in the headers
func notAllowedHandler(x *mux.Router) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
//next.ServeHTTP(w, r)
x.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
var routeMatch mux.RouteMatch
if route.Match(r, &routeMatch) || routeMatch.MatchErr == mux.ErrMethodMismatch {
m, _ := route.GetMethods()
w.Header().Set("Access-Control-Allow-Methods", strings.Join(m, ", "))
}
return nil
})
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
})
}
Edit: I forgot to mention. You need to pass router parameter to your custom method not allowed handler;
You can find the complete example below;
func main() {
r := mux.NewRouter()
r.HandleFunc("/test", func(rw http.ResponseWriter, r *http.Request) {}).Methods(http.MethodPost)
r.HandleFunc("/test2", func(rw http.ResponseWriter, r *http.Request) {}).Methods(http.MethodPut)
r.MethodNotAllowedHandler = notAllowedHandler(r)
http.ListenAndServe(":8089", r)
}
func notAllowedHandler(x *mux.Router) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
x.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
var routeMatch mux.RouteMatch
if route.Match(r, &routeMatch) || routeMatch.MatchErr == mux.ErrMethodMismatch {
m, _ := route.GetMethods()
w.Header().Set("Access-Control-Allow-Methods", strings.Join(m, ", "))
}
return nil
})
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
})
}
I have created middle-wares using Adapter pattern. One of my middle-ware is for authentication. So if the user is not authorized then I have to send back response to the user and the next middle-ware/s should not be called.
// Adapter type
type Adapter func(http.Handler) http.Handler
// Adapt func
func Adapt(h http.Handler, adapters ...Adapter) http.Handler {
// Call all middleware
for _, adapter := range adapters {
h = adapter(h)
}
return h
}
// CheckAuth middleware
func CheckAuth() Adapter {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Get Authorization token from the header
// Validate the token
// if valid token then call h.ServeHTTP(w, r)
// else send response 401 to the user,
if(validUser){
h.ServeHTTP(w, r)
}else{
fmt.Fprintf(w, "Unauthorized")
}
return h
}
}
}
http.Handle("/", Adapt(indexHandler, AddHeader(),
CheckAuth(),
CopyMgoSession(db),
Notify(logger),
)
in the CheckAuth middleware I'm calling h.ServeHTTP(w, r) only if the user is authorized, so for the else condtition we also need to break the for loop of the Adapt function or else it will call next middleware even after sending the response.
let me know if there is any other way to handle such situation.
The next middleware in the chain only runs if you explicitly call it.
That next middleware is passed to your closure as h, and you are calling it by invoking h.ServeHTTP(). If you do not call this, no other middleware runs, so you must supply the complete HTTP response.
The Adapt function is not relevant for serving requests. It is executed once (and only once) before the HTTP server even starts. Note that it returns an http.Handler but it isn't an http.Handler itself.
That handler that Adapt returns in this case behaves like this:
var indexHandler http.Handler
func handlerWithMiddleWare(w http.ResponseWriter, r *http.Request) {
notify := func(w http.ResponseWriter, r *http.Request) {
copyMgoSession := func(w http.ResponseWriter, r *http.Request) {
checkAuth := func(w http.ResponseWriter, r *http.Request) {
addHeader := func(w http.ResponseWriter, r *http.Request) {
indexHandler.ServeHTTP(w, r)
}
addHeader(w, r)
}
checkAuth(w, r)
}
copyMgoSession(w, r)
}
notify(w, r)
}
So if you let CheckAuth return without calling the next middleware, you can send whatever response you like; just as you would in any other handler.
By the way, you way want to let Adapt iterate in reverse order. I'm not sure that you're aware that Notify executes first, then CopyMgoSession, then CheckAuth, and then AddHeader.
Middleware is typically chained. There are frameworks that can do it for you. A sleek example is Alice.
chain := alice.New(th.Throttle, timeoutHandler, nosurf.NewPure).Then(myHandler)
If you want to do it yourself you can use recursion to avoid a for loop. For example (from this link):
// buildChain builds the middlware chain recursively, functions are first class
func buildChain(f http.HandlerFunc, m ...middleware) http.HandlerFunc {
// if our chain is done, use the original handlerfunc
if len(m) == 0 {
return f
}
// otherwise nest the handlerfuncs
return m[0](buildChain(f, m[1:cap(m)]...))
}
Each middleware receives the next as parameter. As such the next has to be manually called by the previous handler otherwise the chain stops. So in your auth middleware you don't have to call the next one if auth fails and the chain stops with your error status being the last thing returned. So in your code you need to accept a parameter of http.Handler and that is the next handler (a middleware function has to have the signature of func(http.Handler) http.Handler). See this blog for more details.
You may want to set the correct http status codes as well. Include something like this:
http.Error(w, "Forbidden: xyz", http.StatusForbidden)
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)
}
}
}
If I were to use the DefaultServeMux (which I designate by passing nil as the second argument to ListenAndServe), then I have access to http.HandleFunc, which you see used below in this example from the Go wiki:
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
In my current code, I am not able to use the DefaultServeMux i.e. I'm passing a custom handler to ListenAndServe
h := &mypackage.Handler{
Database: mydb
}
http.ListenAndServe(":8080", h)
so I don't get the http.HandleFunc built in. However, I have to adapt some authorization code to my code base that requires something like http.HandleFunc. For example, if I had been using DefaultServeMux, when I hit the "/protected" route, I would want to go to the Protected handler, but only after passing through the h.AuthorizationHandlerFunc like this
h.AuthorizationHandlerFunc(Protected)
However, since I'm not using DefaultServeMux, it's not working i.e. I'm not able to pass the Protected function (and have it called) to the AuthorizationHandlerFunc. This is the implementation of the AuthorizationHandlerFunc below. You can see below that Protected never gets called.
Question: how do I implement HandlerFunc in this situation (without using DefaultServeMux)?
func (h *Handler) AuthorizationHandlerFunc(next http.HandlerFunc) http.Handler{
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
h.AuthorizationMiddleWare(w, r, next)
})
}
func (h *Handler) AuthorizationMiddleWare(w http.ResponseWriter, r *http.Request, next http.HandlerFunc){
//other stuff happens
log.Println("this is never getting called")
next(w,r)
}
func (h *Handler)Protected(w http.ResponseWriter, r *http.Request){
log.Println("this is never getting called")
}
Update
ServeHTTP is implemented on mypackage.Handler. Why is the Protected function not getting called, or, for that matter, the relevant code in the AuthorizationMiddleWare?
Re-implement your authorization middleware as a http.Handler :
type auth struct {
DB *sql.DB
UnauthorizedHandler http.Handler
}
func NewAuth(db *sql.DB, unauthorized http.Handler) *auth {
return auth{db, unauthorized}
}
func (a *auth) Protected(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
// Check whether the request is valid
// If it's invalid, call your error func and make sure to *return* early!
if !valid {
a.UnauthorizedHandler.ServeHTTP(w, r)
return
}
// Call the next handler on success
h.ServeHTTP(w, r)
return
}
return http.HandlerFunc(fn)
}
func someHandler(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Hello!\n")
}
func main() {
auth := NewAuth(db, errorHandler)
r := http.NewServeMux()
// We have a http.Handler implementation that wraps a http.HandlerFunc
// ... so we call r.Handle on our ServeMux and type-cast the wrapped func
r.Handle("/protected", auth.Protected(http.HandlerFunc(someHandler)))
// Just a simple http.HandlerFunc here
r.HandleFunc("/public", someOtherHandler)
log.Fatal(http.ListenAndServe(":8000", r))
}
Take a look at the httpauth lib I wrote for a different example with a ServeHTTP method. Both the above and explicitly creating a ServeHTTP method on your type are valid approaches.