Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regex router pattern not matching as expected. #970

Open
mmmmmrob opened this issue Jan 16, 2025 · 2 comments
Open

Regex router pattern not matching as expected. #970

mmmmmrob opened this issue Jan 16, 2025 · 2 comments

Comments

@mmmmmrob
Copy link

mmmmmrob commented Jan 16, 2025

This is probably me doing something wrong, as I'm new to go and chi, but any help would be really appreciated!

I am serving a URL in the form /test/f9772163-44e7-49b1-9c8c-36b8d023aa6b-2024-01-01 which is a UUID and date, separated by a -.

I hoped I could match this using a regex pattern in the router pattern string, but this fails to match.

r.Get("/test/{UUID:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}}-{date}", func(w http.ResponseWriter, r *http.Request) {
	uuidParam := chi.URLParam(r, "UUID")
	dateParam := chi.URLParam(r, "date")
	w.Write([]byte(uuidParam + "\n" + dateParam))
})

Expected:

f9772163-44e7-49b1-9c8c-36b8d023aa6b
2024-01-01

Actual:

404 page not found
@mmmmmrob
Copy link
Author

Digging further:

If I remove the date and only keep the trailing - I see the following:

Request /test/f9772163-44e7-49b1-9c8c-36b8d023aa6b-

// The trailing hyphen is WITHIN the uuid regex
r.Get("/test/{UUID:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}-}", func(w http.ResponseWriter, r *http.Request) {
	uuidParam := chi.URLParam(r, "UUID")
	w.Write([]byte(uuidParam))
})

// successfully matches and returns f9772163-44e7-49b1-9c8c-36b8d023aa6b-
// The trailing hyphen is OUTSIDE the uuid regex
r.Get("/test/{UUID:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}}-", func(w http.ResponseWriter, r *http.Request) {
	uuidParam := chi.URLParam(r, "UUID")
	w.Write([]byte(uuidParam))
})

// fails to match and returns 404 page not found

@EdenBW
Copy link

EdenBW commented Jan 30, 2025

Chi looks for explicit literal matches (in this case the "-" between your UUID and date) before the group patterns, so it doesn't really know where any of the -'s belong.

You could use a different separator like "/" or "_", or write the URL parser yourself.

package main

import (
	"fmt"
	"log"
	"net/http"
	"strings"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
)

func main() {
	r := chi.NewRouter()
	r.Use(middleware.Logger)

	// works when "-" appears only in 1 group
	r.Get("/test1/{group1:[0-9]{1}}-{group2:[0-9]{1}}", func(w http.ResponseWriter, r *http.Request) {
		group1 := chi.URLParam(r, "group1")
		group2 := chi.URLParam(r, "group2")
		w.Write([]byte(fmt.Sprintf("This works!\ngroup1: %s\ngroup2: %s\n", group1, group2)))
	})

	// works with multiple "-" but using "_" as a group separator
	r.Get("/test2/{group1:[0-9]{1}-[0-9]{1}}_{group2:[0-9]{1}}", func(w http.ResponseWriter, r *http.Request) {
		group1 := chi.URLParam(r, "group1")
		group2 := chi.URLParam(r, "group2")
		w.Write([]byte(fmt.Sprintf("This also works!\ngroup1: %s\ngroup2: %s\n", group1, group2)))
	})

	// won't work with a "-" appearing in a group AND as a separator
	r.Get("/test3/{group1:[0-9]{1}-[0-9]{1}}-{group2:[0-9]{1}}", func(w http.ResponseWriter, r *http.Request) {
		group1 := chi.URLParam(r, "group1")
		group2 := chi.URLParam(r, "group2")
		w.Write([]byte(fmt.Sprintf("This won't be reached!\ngroup1: %s\ngroup2: %s\n", group1, group2)))
	})

	// won't work with a "-" appearing in multiple groups
	r.Get("/test4/{group1:[0-9]{1}-[0-9]{1}}{group2:-[0-9]{1}}", func(w http.ResponseWriter, r *http.Request) {
		group1 := chi.URLParam(r, "group1")
		group2 := chi.URLParam(r, "group2")
		w.Write([]byte(fmt.Sprintf("This won't be reached!\ngroup1: %s\ngroup2: %s\n", group1, group2)))
	})

	// Optionally parse it yourself
	r.Get("/test5/{params}", func(w http.ResponseWriter, r *http.Request) {
		params := chi.URLParam(r, "params")
		parts := strings.Split(params, "-")
		group1 := strings.Join(parts[:2], "-")
		group2 := strings.Join(parts[2:], "-")
		w.Write([]byte(fmt.Sprintf("This walso works!\ngroup1: %s\ngroup2: %s\n", group1, group2)))
	})

	fmt.Println("Server starting on :3000")
	fmt.Println("Test URLs:")
	fmt.Println("1. http://localhost:3000/test1/1-1  (will work)")
	fmt.Println("2. http://localhost:3000/test2/1-1_1 (will work)")
	fmt.Println("3. http://localhost:3000/test3/1-1-1 (won't work)")
	fmt.Println("4. http://localhost:3000/test4/1-1-1 (won't work)")
	fmt.Println("5. http://localhost:3000/test5/1-1-1 (will work)")

	log.Fatal(http.ListenAndServe(":3000", r))
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants