http.HandleFunc模式中的通配符


75

在Go(语言)中注册处理程序时,是否可以在模式中指定通配符?

例如:

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

其中*可以是任何有效的URL字符串。还是唯一的解决方案是/groups从处理程序(peopleInGroupHandler)函数内部匹配并找出其余部分?


我可以看看您输入字符串的示例吗,请问您当前正在获取匹配项吗?

请参阅goweb.googlecode.com,该语言在Go中提供了Ruby on Rails样式路线的支持-即goweb.MapFunc(“ / people / {person_id} / groups / {group_id}”,处理程序)
Mat Ryer

Answers:


96

http.Handler和http.HandleFunc的模式不是正则表达式或glob。无法指定通配符。它们记录在这里

也就是说,创建自己的可以使用正则表达式或所需的任何其他模式的处理程序并不难。这是一个使用正则表达式(已编译,但未经测试)的表达式:

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)
}

2
当我想在Go中使用ruby在rails样式的路线上时,我启动了goweb项目(请参阅goweb.googlecode.com),该项目允许这种样式的路线映射:goweb.MapFunc(“ / people / {person_id}”,handler)
Mat Ryer

3
Gorillatoolkit在实现用于处理路由的PAT和MUX方面做得很好。唯一的问题是它运行缓慢,我尚未审查代码。至少在他们的API中,可以命名参数...这就是这类功能的全部重点。上面的代码没有提供任何复杂的内容,没有命名元素就可能没有用。
理查德

8
如果您只是在寻找“其他”之*类的the pattern "/" matches all paths not matched by other registered patterns
包罗万象的内容

也许(h *RegexpHandler) Handler应该是(h *RegexpHandler) Handle?在这里看到:golang.org/pkg/net/http/#ServeMux.Handle :)
Coaku

1
如何将代码集成到的原始示例中net/http。你能举一个完整的例子吗?谢谢。
卡斯珀

59

自2011年以来,您现在(2014年以上)可以找到其他解决方案。
例如,大猩猩Web工具包mux软件包提供了所有种类的路由选项:

  • 请求路径上的模式匹配,带有可选的正则表达式。
  • 在URL主机和方案,请求方法,标头和查询值上匹配。
  • 根据自定义功能进行匹配。
  • 使用子路由器进行简单的嵌套路由。

它可以很容易地集成到任何BYOR(带上自己的路由器)http库,例如negroni

这是文章“大猩猩与帕特与路线:多人对决”的示例:

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))
}

有时最好不要只使用另一个“魔术”包,而要了解幕后的情况

在这种情况下,“魔术”定义为“ gorilla/mux/regexp.go”,并在此处进行测试
这个想法是提取命名变量,组装要匹配的正则表达式,创建“反向”模板以构建URL,并编译正则表达式以验证URL构建中使用的变量值。


因为它是新的。给它时间:)
弗拉德·黑斑羚

5
因为有时最好不要只使用另一个“魔术”包,而要了解
幕后的情况

7

我只想添加julienschmidt/httprouter,其行为类似于,net/http但具有用于url值的附加参数和对请求方法的支持:

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))
}

它似乎也比gorilla/mux(根据GitHub)更受欢迎,并且它还声称需要更少的内存。

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


3

这是一个如何使用@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)
}

1

您可以检查violetear如何处理动态+综合(通配符)模式,这仅是补充示例:

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")

在这种情况下,请求可能有2个不同 UUIDS

对于动态/通配符,可以应用:

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

正则表达式可用于匹配IP:

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

或者只是仅捕获所有允许GETHEAD方法:

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

可以在这里找到更多示例:https : //violetear.org/post/how-it-works/


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.