Search code examples
gohttprouteshandler

Go 1.22 net/http group routing


I am creating an API and would like to separate the Endpoints by feature, having seen that the net/http packet has received an update so I wanted to try using it.

The /v1/user endpoint gives me problems and does not return the expected data, here are some curl that may help:

$ curl -X 'GET' 'http://localhost:5000/v1/user/john'      
GET /v1/user/{username}

$ curl -X 'GET' 'http://localhost:5000/v1/user/'
GET /v1/user

$ curl -X 'GET' 'http://localhost:5000/v1/user'
<a href="/user/">Moved Permanently</a>.

Here is my current code (I removed the superfluous endpoints):

// will be moved of the user.go file
userRouter := http.NewServeMux()
userRouter.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("GET /v1/user"))
})
userRouter.HandleFunc("GET /{username}", func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("GET /v1/user/{username}"))
})

// will remain inside api.go
v1 := http.NewServeMux()
v1.Handle("/user/", http.StripPrefix("/user", userRouter))

main := http.NewServeMux()
main.Handle("/v1/", http.StripPrefix("/v1", v1))

server := &http.Server{ Addr: "localhost:5000", Handler: main }
server.ListenAndServe()

What i tried:

  • I tried changing to endpoint path from "GET /" to "GET" or to "", but obviously the syntax is wrong

  • I tried to change the userRouter Handler path from "/user/" to "/user", doing this causes all the endpoint to stop working

$ curl -X 'GET' 'http://localhost:5000/v1/user/john'
404 page not found

$ curl -X 'GET' 'http://localhost:5000/v1/user/'    
404 page not found

$ curl -X 'GET' 'http://localhost:5000/v1/user'
<a href="/">Moved Permanently</a>.
  • whereas still with the change from before, but removing the http.StripPrefix to the userRounter router, and adding /user to the endpoints; the only working endpoint is the /v1/user. Code:
userRouter := http.NewServeMux()
userRouter.HandleFunc("GET /user", func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("GET /v1/user"))
})
userRouter.HandleFunc("GET /user/{username}", func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("GET /v1/user/{username}"))
})

v1 := http.NewServeMux()
v1.Handle("/user", userRouter)

main := http.NewServeMux()
main.Handle("/v1/", http.StripPrefix("/v1", v1))

server := &http.Server{ Addr: "localhost:5000", Handler: main }
server.ListenAndServe()

Requests:

$ curl -X 'GET' 'http://localhost:5000/v1/user/john'
404 page not found

$ curl -X 'GET' 'http://localhost:5000/v1/user/'    
404 page not found

$ curl -X 'GET' 'http://localhost:5000/v1/user'     
GET /v1/user

Solution

  • issue here is v1.Handle("/user", userRouter) you forgot to add /

    package main
    
    import (
        "net/http"
    )
    
    func main() {
        userRouter := http.NewServeMux()
        userRouter.HandleFunc("GET /user", func(w http.ResponseWriter, r *http.Request) {
            w.Write([]byte("GET /v1/user"))
        })
        userRouter.HandleFunc("GET /user/{username}", func(w http.ResponseWriter, r *http.Request) {
            w.Write([]byte("GET /v1/user/{username}"))
        })
    
        v1 := http.NewServeMux()
        v1.Handle("/user/", userRouter)
        v1.Handle("/user", userRouter)
        main := http.NewServeMux()
        main.Handle("/v1/", http.StripPrefix("/v1", v1))
    
        server := &http.Server{Addr: "localhost:5000", Handler: main}
        server.ListenAndServe()
    }
    

    I would recommend that you can write simple routing code like this

    package main
    
    import (
        "net/http"
    )
    
    func main() {
    
        v1 := http.NewServeMux()
        v1.HandleFunc("/v1/user", func(w http.ResponseWriter, r *http.Request) {
            w.Write([]byte("GET /v1/user"))
        })
    
        v1.HandleFunc("GET /v1/user/{username}", func(w http.ResponseWriter, r *http.Request) {
            username := r.PathValue("username")
            w.Write([]byte("GET /v1/user/" + username))
        })
    
    
        server := &http.Server{Addr: "localhost:5000", Handler: v1}
        server.ListenAndServe()
    }