In this Youtube video (at around 15:29) of a Golang talk by Blake Mizerany, he talks about how to build a router without using a third party package, covering in detail how to construct a route that has a variable component, such as an id. This is the handler that he uses, with the first line showing how to get the variable component of the route (i.e. the key)
func productHandler(w http.ResponseWriter, r *http.Request){
key := r.URL.Path[len("/products/":]
switch r.Method{
case "GET":
//do stuff
case "POST"
//do stuff
default:
http.Error(w, "method not allowed", 405)
}
}
It's not clear from his presentation though what his actual route looks like.
I'm trying to build a route that handles a put request with an id. When I click an element on my page, it sends a put request to this route
http://localhost:8080/products/1433255183951
I have a route like this
http.HandleFunc("/products/{id}", doSomethingWithProduct){
}
and of course have the func
func doSomethingWithProduct(res http.ResponseWriter, req *http.Request{
key := req.URL.Path[len("/products/"):]
log.Println(key, "is this logging?? nope")
}
Problem. Even though I have that route set up, and the handler, when I click the element I got a 404 not found, and there's no indication that my function was called (i.e. it's not logging)
Question: How do I create a custom route/func that handles a PUT request to
http://localhost:8080/products/1433255183951
http.HandleFunc
does not handle the "capture groups" like you're trying to do with {id}
.
http.HandleFunc("/products/", handler)
will match all routes that begin with this pattern. You have to parse the rest of it yourself.
See ServeMux.
ServeMux is an HTTP request multiplexer. It matches the URL of each incoming request against a list of registered patterns and calls the handler for the pattern that most closely matches the URL.
Patterns name fixed, rooted paths, like "/favicon.ico", or rooted subtrees, like "/images/" (note the trailing slash). Longer patterns take precedence over shorter ones, so that if there are handlers registered for both "/images/" and "/images/thumbnails/", the latter handler will be called for paths beginning "/images/thumbnails/" and the former will receive requests for any other paths in the "/images/" subtree.
Note that since a pattern ending in a slash names a rooted subtree, the pattern "/" matches all paths not matched by other registered patterns, not just the URL with Path == "/".
Patterns may optionally begin with a host name, restricting matches to URLs on that host only. Host-specific patterns take precedence over general patterns, so that a handler might register for the two patterns "/codesearch" and "codesearch.google.com/" without also taking over requests for "http://www.google.com/".
ServeMux also takes care of sanitizing the URL request path, redirecting any request containing . or .. elements to an equivalent .- and ..-free URL.
HandleFunc
doesn't know what to do with {id}
. Give it a specific path it can match:
http.HandleFunc("/products/", doSomethingWithProduct)
The builtin HTTP router doesn't do anything fancy like binding parameters; however, you can specify entire prefixes to associate with a handler. See the documentation for http.ServeMux
.
Try something like this:
func main() {
productsPrefix := "/products/"
http.HandleFunc(productsPrefix, func(w http.ResponseWriter, r *http.Request) {
if (r.Method == "PUT") && (strings.Index(r.URL.Path, productsPrefix) == 0) {
productId := r.URL.Path[len(productsPrefix):]
fmt.Printf("OK: %s %s productId=%s
", r.Method, r.URL.Path, productId)
}
})
log.Print("Listening on localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
For example:
$ curl -XPUT http://localhost:8080/products/1234
# => OK: PUT /products/1234 productId=1234