go-chi / chi

lightweight, idiomatic and composable router for building Go HTTP services
https://go-chi.io
MIT License
18.51k stars 988 forks source link

Broken chi.URLParam return empty string #938

Open vorotech opened 3 months ago

vorotech commented 3 months ago

Reading the release notes, I assume the issue might be introduced in 5.0.12

Here is the code to demostrate

r.Route("/invoices", func(r chi.Router) {
      r.Route("/{invoiceNumber}", func(r chi.Router) {
          r.Use(func(next http.Handler) http.Handler {
              return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                  invoiceNumber := chi.URLParam(r, "invoiceNumber")
                  fmt.Println("Invoice Number in Middleware:", invoiceNumber) // Debugging line
                  ctx := context.WithValue(r.Context(), InvoiceCtxKey, invoiceNumber)
                  next.ServeHTTP(w, r.WithContext(ctx))
              })
          })
          r.Use(h.InvoiceCtx)
          r.Get("/", h.getInvoice)       // GET /invoices/123
          r.Delete("/", h.deleteInvoice) // DELETE /invoices/123
      })
})

Specifically the line invoiceNumber := chi.URLParam(r, "invoiceNumber") will work as expected.

However, you can see the usage of r.Use(h.InvoiceCtx) where the same line of code is present - it will return "" emptry string every time.

func (h *Handler) InvoiceCtx(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        var invoice *domain.Invoice
        var err error

        if invoiceNumber := chi.URLParam(r, "invoiceNumber"); invoiceNumber != "" {
            invoice, err = h.invoices.GetInvoice(invoiceNumber)
        } else {
            render.Render(w, r, ErrNotFound)
            return
        }
        if err != nil {
            render.Render(w, r, ErrNotFound)
            return
        }

        ctx := context.WithValue(r.Context(), InvoiceCtxKey, invoice)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

I don't understand the difference between usage the inline function with r.Use and actually specifying the fucntion, but it has a different execution behaviour.

Finally, if inside the InvoiceCtx replace chi.URLParam(r, "invoiceNumber") usage with r.PathValue("invoiceNumber") everything working as expected again.

ConsoleTVs commented 2 months ago

I have found out that chi.URLParam does not work and request.PathValue does...