在 Go(语言)中注册处理程序时,有没有办法在模式中指定通配符?
例如:
http.HandleFunc("/groups/*/people", peopleInGroupHandler)
其中 *
可以是任何有效的 URL 字符串。或者是匹配 /groups
并从处理程序 (peopleInGroupHandler
) func 中找出其余部分的唯一解决方案?
http.Handler 和 http.HandleFunc 的模式不是正则表达式或 glob。没有办法指定通配符。它们被记录在案 here。
也就是说,创建自己的可以使用正则表达式或任何其他类型的模式的处理程序并不难。这是使用正则表达式的一个(已编译,但未经测试):
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)
}
自 2011 年以来,您现在(2014 年以上)可以找到其他解决方案。
例如,mux package of the Gorilla Web toolkit 提供了各种路由选项:
请求路径上的模式匹配,带有可选的正则表达式。
匹配 URL 主机和方案、请求方法、标头和查询值。
基于自定义函数的匹配。
使用子路由器轻松嵌套路由。
它可以轻松集成到任何 BYOR(自带路由器)http 库 like negroni。
以下是文章“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))
}
有时最好不要只使用另一个“神奇”包,而是了解引擎盖下发生了什么
在本例中,“魔术”在“gorilla/mux/regexp.go
”和 tested here 中定义。
其想法是提取命名变量,组装要匹配的正则表达式,创建“反向”模板来构建 URL 和编译正则表达式以验证 URL 构建中使用的变量值。
我只是想添加 julienschmidt/httprouter
,它的行为类似于 net/http
,但带有一个用于 url-values 的附加参数并支持请求方法:
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
这是如何使用来自@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)
}
RegexpHandler
的不同处理程序,而不是 http.HandleFunc
。
您可以检查 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")
或者只是一个只允许 GET
和 HEAD
方法的全部内容:
router.HandleFunc("/command/ping/*", anyHandler, "GET, HEAD")
可在此处找到更多示例:https://violetear.org/post/how-it-works/
*
的包罗万象,the docs 需要注意的是the pattern "/" matches all paths not matched by other registered patterns
(h *RegexpHandler) Handler
应该是(h *RegexpHandler) Handle
?见这里:golang.org/pkg/net/http/#ServeMux.Handle :)net/http
的原始示例中。你能举一个完整的例子吗?谢谢。