#getting "panic: http: multiple registrations for /user "

28 messages ยท Page 1 of 1 (latest)

radiant sorrel
#

I am getting this error because I have registered the same endpoint twice. But, I want to have them with different methods.

GET for fetching data 
POST for adding new data.

Any idea how to implement this?

package main

import (
    "fmt"
    "net/http"
)

func usrGet(w http.ResponseWriter, r *http.Request) {
    fmt.Println("GET user")
    w.Write([]byte("GET user"))
}

func usrAdd(w http.ResponseWriter, r *http.Request) {
    fmt.Println("add User")
    w.Write([]byte("add user"))
}

func main() {
    mux := NewRoute()

    mux.Endpoint("GET", "/user", usrGet)
    mux.Endpoint("POST", "/user", usrAdd)

    http.ListenAndServe(":8888", mux)
}
type Route struct {
    *http.ServeMux
}

func NewRoute() *Route {
    return &Route{http.NewServeMux()}
}

func (r *Route) Endpoint(verb, path string, handler http.HandlerFunc) {
    r.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {  // <<<< The issue comes from this line
        if r.Method != verb {
            fmt.Println("method:", r.Method)
            w.WriteHeader(http.StatusBadRequest)
            return
        }

        handler(w, r)
    })

}

func (r *Route) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    r.ServeHTTP(w, req)
}
serene fable
#

welcome to the standard http library ๐Ÿ˜… You can't do this the way you're trying to; http.ServeMux doesn't allow duplicates, as you've discovered, and it only accounts for the path. Most people using stdlib use something like this:

func handle(w http.ResponseWriter, req *http.Request) {
  switch req.Method {
    case http.MethodGet:
      getHandler()
    case http.MethodPost:
      postHandler()
  }
}
#

so, if you want to use standard HTTP, I might suggest passing a map[string]http.Handler in Endpoint(s). You have to register all methods for a path simultaneously, but it'd work.

radiant sorrel
#

well in Gorilla Mux can do that. But they have different matching system

serene fable
#

yes it can and it is extraordinarily annoying that the standard can't, but here we are

#

unless I missed something big, there's no workaround, you just have to manage methods in the handler.

#

follow up question: if you know Gorilla can do this, why are you using stdlib?

radiant sorrel
radiant sorrel
#

but i prefer to use the standard library.

serene fable
#

sure. for example,

func (r *Route) Endpoint(path string, verbs map[string]http.HandlerFunc) {
    r.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
        for verb, handler := range verbs {
          if verb == r.Method {
            handler(w, r)
            return
          }
        }
        fmt.Println("method:", r.Method)
        w.WriteHeader(http.StatusBadRequest)
    })
}

this is not good for a variety of reasons but it demonstrates the principle

serene fable
radiant sorrel
serene fable
#

The standard HTTP library routing is really fast. I wouldn't worry about it.

#

Most routing libraries shoot to provide enhanced features rather than outperform the standard--turns out that's really hard to do

#

Gin uses no memory when routing and still, if I remember correctly, is slower than standard. It just also has methods

radiant sorrel
#

well I don't like third-party packages because

  1. targeted everyone's use and bloated/heavy
  2. has a lot of sacrifices in performance.
  3. you never know in those thousand features, where the security hole exists.
  4. and good luck fixing the security hole if even you can find it ๐Ÿ˜„
  5. like gorilla mux they will leave you alone middle of the project.
#

so yea. since std provided, but a bit harder, why not use it?

radiant sorrel
serene fable
serene fable
radiant sorrel
#

wait, are we going to still call the endpoints like this. with the map[string]http.Handler difference?

    mux.Endpoint("GET", "/user", usrGet)
    mux.Endpoint("POST", "/user", usrAdd)
serene fable
#

You'd do

mux.Endpoint("/user", map[string]http.HandlerFunc{
  "GET": usrGet,
  "POST": usrAdd,
})
radiant sorrel
#

i think we got infinite loop that generates so many goroutines here.

$ go run .                                                                                                                        
runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0xc020180380 stack=[0xc020180000, 0xc040180000]
serene fable
#

I'll say.

radiant sorrel
serene fable
#

You could

type Route struct {
  muxs map[string]*http.ServeMux
}

func (r *Route) Endpoint(verb, path string, handler http.HandlerFunc) {
  // Get ServeMux for this verb
  mux, ok := r.muxs[verb];
  if !ok {
    // If there isn't one, make it
    r.muxs[verb] = http.NewServeMux()
    mux = r.muxs[verb]
  }
  mux.HandleFunc(path, ...) 
}