ChatGPT解决这个技术问题 Extra ChatGPT

Wildcards in the pattern for http.HandleFunc

When registering handlers in Go (language) is there any way to specify wildcards in the pattern?

For example:

http.HandleFunc("/groups/*/people", peopleInGroupHandler)

Where the * could be any valid URL string. Or is the only solution to match /groups and figure the rest out from within the handler (peopleInGroupHandler) func?

May I see an example of your input string, and what you're currently getting for matches please?
See goweb.googlecode.com that provides support for Ruby on Rails style routes in Go - i.e. goweb.MapFunc("/people/{person_id}/groups/{group_id}", handler)

E
Evan Shaw

The patterns for http.Handler and http.HandleFunc aren't regular expressions or globs. There isn't a way to specify wildcards. They're documented here.

That said, it's not too hard to create your own handler that can use regular expressions or any other kind of pattern you want. Here's one that uses regular expressions (compiled, but not tested):

type route struct {
    pattern *regexp.Regexp
    handler http.Handler
}

type RegexpHandler struct {
    routes []*route
}

func (h *RegexpHandler) Handler(pattern *regexp.Regexp, handler http.Handler) {
    h.routes = append(h.routes, &route{pattern, handler})
}

func (h *RegexpHandler) HandleFunc(pattern *regexp.Regexp, handler func(http.ResponseWriter, *http.Request)) {
    h.routes = append(h.routes, &route{pattern, http.HandlerFunc(handler)})
}

func (h *RegexpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    for _, route := range h.routes {
        if route.pattern.MatchString(r.URL.Path) {
            route.handler.ServeHTTP(w, r)
            return
        }
    }
    // no pattern matched; send 404 response
    http.NotFound(w, r)
}

As I wanted ruby on rails style routes in Go, I have started the goweb project (see goweb.googlecode.com) that allows this style of route mapping: goweb.MapFunc("/people/{person_id}", handler)
Gorillatoolkit does a great job of implementing PAT and MUX for processing routes. The only problem is that it is slow and I have yet to review the code. At least in their API it's possible to name the params... which is the whole point of that sort of functionality. The code above does not provide anything that sophisticated and is likely not useful without named elements.
If you're merely looking for an "otherwise" *-like catch-all, something to note from the docs is that the pattern "/" matches all paths not matched by other registered patterns
Maybe (h *RegexpHandler) Handler should be (h *RegexpHandler) Handle? See here: golang.org/pkg/net/http/#ServeMux.Handle :)
How can I integrate the code into the original example of net/http. Can you give a complete example? Thx.
V
VonC

Since 2011, you can now (2014+) find other solutions.
For instance, the mux package of the Gorilla Web toolkit provides all kind of routing options:

Pattern matching on request paths, with optional regular expressions.

Matching on URL host and scheme, request method, header and query values.

Matching based on custom functions.

Use of sub-routers for easy nested routing.

It can be easily integrated to any BYOR (Bring your own Router) http library, like negroni.

Here is an example from the article "Gorilla vs Pat vs Routes: A Mux Showdown":

package main

import (
  "github.com/gorilla/mux"
  "log"
  "net/http"
)

func main() {
  rtr := mux.NewRouter()
  rtr.HandleFunc("/user/{name:[a-z]+}/profile", profile).Methods("GET")

  http.Handle("/", rtr)

  log.Println("Listening...")
  http.ListenAndServe(":3000", nil)
}

func profile(w http.ResponseWriter, r *http.Request) {
  params := mux.Vars(r)
  name := params["name"]
  w.Write([]byte("Hello " + name))
}

Sometimes better not to just use yet another "magic" package, but understand what's going on under the hood

In this instance, the "magic" is defined in "gorilla/mux/regexp.go", and tested here.
The idea is to extract named variables, assemble a regexp to be matched, create a "reverse" template to build URLs and compile regexps to validate variable values used in URL building.


Because it is much newer. Give it time :)
Because sometimes better not to just use yet another "magic" package, but understand what's going on under the hood :)
D
Dennis Stritzke

I just wanted to add julienschmidt/httprouter, which just behaves like net/http but with an additional parameter for url-values and support for request methods:

https://github.com/julienschmidt/httprouter

package main

import (
    "fmt"
    "github.com/julienschmidt/httprouter"
    "net/http"
    "log"
)

func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
    fmt.Fprint(w, "Welcome!\n")
}

func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
}

func main() {
    router := httprouter.New()
    router.GET("/", Index)
    router.GET("/hello/:name", Hello)

    log.Fatal(http.ListenAndServe(":8080", router))
}

It also seems to be slightly more popular than gorilla/mux (according to GitHub) and it also claims to need less memory.

https://github.com/julienschmidt/go-http-routing-benchmark


c
caike

Here's an example of how to use the code example from @evanshaw

func handleDigits(res http.ResponseWriter, req *http.Request) {
    res.Write([]byte("Digits in the URL\n"))
}

func handleStrings(res http.ResponseWriter, req *http.Request) {
    res.Write([]byte("Strings in the URL\n"))
}

func main() {
    handler := &RegexpHandler{}

    reg1, _ := regexp.Compile("/foo-\\d+")
    handler.HandleFunc(reg1, handleDigits)

    reg2, _ := regexp.Compile("/foo-\\w+")
    handler.HandleFunc(reg2, handleStrings)

    http.ListenAndServe(":3000", handler)
}

cannot use reg2 (type *regexp.Regexp) as type string i│ n argument to http.HandleFunc
@CSQGB If you read the answer properly it says "Here's an example of how to use the code example from @evanshaw" its not just code. This uses an example from above. It even uses a different handler called RegexpHandler, not an http.HandleFunc.
n
nbari

You could check how violetear handles dynamic + catchall (wildcard) patterns, this is just for complement for example:

uuid := `[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}`
router.AddRegex(":uuid")
router.HandleFunc("/test/:uuid/:uuid", handleUUID, "GET,HEAD")

In this case, the request may have 2 different UUIDS

For a dynamic/wildcard this could apply:

http://api.violetear.org/command/ping/127.0.0.1
                        \______/\___/\________/
                            |     |      |
                             static      |
                                      dynamic

A regex may be used to match the IP:

router.AddRegex(":ip", `^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$`)
router.HandleFunc("/command/ping/:ip", ipHandler, "GET")

Or simply just a catch all allowing GET and HEAD methods only:

router.HandleFunc("/command/ping/*", anyHandler, "GET, HEAD")

More examples can be found here: https://violetear.org/post/how-it-works/