Подстановочные знаки в шаблоне для http.HandleFunc
При регистрации обработчиков в Go (language) есть ли способ указать подстановочные знаки в шаблоне?
Например:
http.HandleFunc("/groups/*/people", peopleInGroupHandler)
Где *
может быть любая допустимая строка URL. Или это единственное решение для соответствия /groups
и вычисление остального изнутри обработчика (peopleInGroupHandler
) func?
Ответы
Ответ 1
Шаблоны для http.Handler и http.HandleFunc не являются регулярными выражениями или глобусами. Не существует способа указать подстановочные знаки. Они документированы здесь.
Тем не менее, не слишком сложно создать собственный обработчик, который может использовать регулярные выражения или любой другой тип шаблона, который вы хотите. Здесь тот, который использует регулярные выражения (скомпилированные, но не протестированные):
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
С 2011 года вы можете (2014+) найти другие решения.
Например, пакет пакета мультиязыков Gorilla Web предлагает все виды маршрутизации:
- Согласование шаблонов на путях запросов с необязательными регулярными выражениями.
- Соответствие хосту и схеме URL, методу запроса, значениям заголовка и запроса.
- Соответствие на основе пользовательских функций.
- Использование под-маршрутизаторов для легкой вложенной маршрутизации.
Он может быть легко интегрирован в любую http-библиотеку BYOR (Bring your own Router), как negroni.
Вот пример из статьи "Gorilla vs Pat vs Routes: 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
" и здесь протестирована.
Идея состоит в том, чтобы извлекать именованные переменные, собирать регулярное выражение для соответствия, создавать "обратный" шаблон для создания URL-адресов и компиляции регулярных выражений для проверки значений переменных, используемых в построении URL-адреса.
Ответ 3
Я просто хотел добавить 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
Ответ 4
Вы можете проверить, как violetear обрабатывает шаблоны dynamic + catchall (wildcard), это просто для дополнения, например:
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/
Ответ 5
Вот пример того, как использовать пример кода из @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)
}
Ответ 6
Бего, ответ на все вопросы веб-сервера Golang. Wetalk - это блог-сайт, построенный на Beego.