3

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

1 Answer 1

8

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)
}
Sign up to request clarification or add additional context in comments.

8 Comments

I had used h.ServeHTTP(w, r) inside the middleware. But it's only after trying to retrieve url parameters. So, that's why it didn't work. So, the answer should be also using h.ServeHTTP(w, r) before the fmt.Println(chi.URLParam(r, "id")) no ?
yes, that one will works.
Thanks. But this always returns success, right ? I updated the code block in question, there I need to check the id conditionally and return an error. So, that can't be done, in this case, can it ?
no that can't be done if you write your h.ServeHTTP the id check. It is not a good thing, to run the code first then check for validation. To work around it, I have other solution, wrap your RequestPlatformVersion with your middleware in the r.Get instead of r.Use (see the UPDATE part), or you can just update your route to r.Route("/platform/{id}", ...) so you can include the middleware, and h.ServeHTTP() after the validation
The reason I tried to use middleware was the check should go through for all the paths in /platform/{id} . But running that validation inside each routes means, I have to add the same logic in multiple routes, no ?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.