gourlparametersmiddlewarego-chi

How to get url param in middleware go-chi


I use a specific middleware for specific set of routes

r.Route("/platform", func(r chi.Router) {
    r.Use(authService.AuthMiddleware)
    r.Get("/{id}/latest", RequestPlatformVersion)
})

Now how can I access id url param inside this AuthMiddleware middleware

func (s *Service) AuthMiddleware(h http.Handler) http.Handler {
    fn := func(w http.ResponseWriter, r *http.Request) {
        fmt.Println(chi.URLParam(r, "id"))
        id := chi.URLParam(r, "id")
        
        if id > 100 {
          http.Error(w, errors.New("Error").Error(), http.StatusUnauthorized)
          return
        }
    }
    return http.HandlerFunc(fn)
}

However, the id param prints as an empty string even though the middleware is being ran and a specific route is being called


Solution

  • You put your chi.URLParam before the path param {id} and you forgot to put .ServeHTTP(w, r) at the middleware. If you don't put that thing, your request will not go inside the path inside the route.

    this is the working example:

    package main
    
    import (
        "fmt"
        "net/http"
    
        "github.com/go-chi/chi"
    )
    
    func AuthMiddleware(h http.Handler) http.Handler {
        fn := func(w http.ResponseWriter, r *http.Request) {
            fmt.Println(chi.URLParam(r, "id"))
            h.ServeHTTP(w, r)
        }
        return http.HandlerFunc(fn)
    }
    
    func main() {
        r := chi.NewRouter()
    
        r.Route("/platform/{id}", func(r chi.Router) {
            r.Use(AuthMiddleware)
            r.Get("/latest", func(rw http.ResponseWriter, r *http.Request) {
                fmt.Println("here ", chi.URLParam(r, "id")) // <- here
            })
        })
    
        http.ListenAndServe(":8080", r)
    }
    

    I move the {id} to platform/{id} so the middleware got the id path value, and add h.ServeHTTP(w, r) inside the middleware.

    try to access http://localhost:8080/platform/1/latest

    the output will be:

    1
    here  1
    

    UPDATE

    It is not good to run the validation after the code, you must fix the way you define the path, and move the .ServeHTTP after the validation.

    This is the example:

    package main
    
    import (
        "errors"
        "fmt"
        "net/http"
        "strconv"
    
        "github.com/go-chi/chi"
    )
    
    func AuthMiddleware(h http.Handler) http.Handler {
        fn := func(w http.ResponseWriter, r *http.Request) {
            fmt.Printf("Middleware First, id: %+v\n", chi.URLParam(r, "id"))
            id, _ := strconv.Atoi(chi.URLParam(r, "id"))
    
            if id > 100 {
                http.Error(w, errors.New("Error").Error(), http.StatusUnauthorized)
                return
            }
            h.ServeHTTP(w, r)
        }
        return http.HandlerFunc(fn)
    }
    
    func main() {
        r := chi.NewRouter()
    
        // This works too ()
        // r.Route("/platform/{id}", func(r chi.Router) {
        //  r.Use(AuthMiddleware)
        //  r.Get("/latest", func(rw http.ResponseWriter, r *http.Request) {
        //      fmt.Println("second: ", chi.URLParam(r, "id")) // <- here
        //  })
        // })
    
        // Other Solution (Wrapping Middleware)
        r.Route("/platform", func(r chi.Router) {
            r.Get("/{id}/latest", AuthMiddleware(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
                fmt.Println("second: ", chi.URLParam(r, "id")) // <- here
            })).ServeHTTP)
        })
    
        http.ListenAndServe(":8080", r)
    }