0
votes

I need to wrap the GetAssetsCompute function inside a middleware

r.Handle("/api/v1/assets/ComputeBlade", GetAssetsCompute(assetService)).Methods("GET")

func GetAssetsCompute(assetService ServiceType) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        //  stuff here
    })
}

but because middlewares take HTTP handlers as an argument and my function is not a handler, I can't.

I was thinking of doing something like this.

func GetAssetsCompute(assetService ServiceType) http.Handler {
        return MyMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            //  stuff here
        }))
    }


func MyMiddleware(next http.Handler) http.Handler {

}

Is this correct? Or is there a better way to do this. Also inside the middleware, I need to access the URL endpoint, do some processing and store this processed value and then again access that in the main handler. How can I do that?

EDIT: I want to apply this middleware to only a subset(>1) of endpoints I have. Not all

I also require the assetService variable used in GetAssetsCompute(assetService ServiceType) function in the handler. So, I need this closure too.

1
When you say access and store the "endpoint" what do you mean? If you just mean the request path you can get that in both the middleware and handler using r.RequestURI.phonaputer
And just to ask, if this is only for one endpoint why do you want to implement it as a middleware?phonaputer
this is not the only one. there are other endpoints too. but apart from these(the ones for which I want to use the middleware), there are endpoints for which I don't want to use. so if m endpoints are there, then n of them uses this middleware, rest m-n do not.ray an
It becomes a function of what middleware is expected to do. If it is highly dependent on what your main handler is doing it might make sense to just add code into the main handler than trying to build a middleware. Middleware are usually used for functions such as logging, auth, metrics capture etc. Here it is to an extent independent of main handler.praveent
Agreed with praveent. I think you have a few options depending on the exact function of the middleware/code. Let me try and make an answer with some of them.phonaputer

1 Answers

2
votes

It seems you are trying to do 2 things. 1 - Apply a middleware to only some of your request handlers. 2 - Pass data from your middleware to your request handlers.

For the first one, I can think of three options. The first is what you are doing now, having a Middleware function in which you wrap your handler functions when you pass them to r.Handle. Pseudocode:

r.Handle("/path1", Mware(Handler1())).Methods("GET")
r.Handle("/path2", Mware(Handler2())).Methods("GET")
r.Handle("/path3-nomiddleware", Handler3()).Methods("GET")

The second thing you could do is to add code to your middleware to filter based on URI path and then register your middleware using r.Use. Pseudocode:

const mwarePaths []string = ...
func Mware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
       if r.RequestURI is in mwarePaths {
          // do the middleware
       }
    }
}

r.Use(Mware)

Thirdly, you could put the code in a function which you call directly in your handlers and not register it like a middleware. Pseudocode:

func myUtil(w http.ResponseWriter, r *http.Request){ ... }

func GetAssetsCompute(assetService ServiceType) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
       myUtil(w, r)

       //  stuff here
    })
}

For the second thing - passing data from middleware to request handlers - here are some ideas.

First, if you go with the regular-function, no-middleware setup above, this problem disappears because anything you need in your handler can simply be a return value from your function.

If you do use a middleware, you can use the context library (also from gorilla) to tie variables to an http.Request instance for passing to your handler: http://www.gorillatoolkit.org/pkg/context . Using that looks like this:

import "github.com/gorilla/context"

func middleware(...) {
   return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
       context.Set(r, "myKey", "bar")
    }
}

func handler(w http.ResponseWriter, r *http.Request) {
   val, ok := context.GetOk(r, "myKey") // returns "bar", true
}

Which of these options you choose to use is up to you (you know your needs). But, as mentioned in the comments, a good rule of thumb would be that code which handles unrelated concerns to what your request handlers do can be middleware. Code which handles concerns that are directly related to what your request handlers are doing can go directly in the handlers.