web-dev-qa-db-ja.com

http.HandleFuncのパターンのワイルドカード

Go(言語)でハンドラーを登録するときに、パターンにワイルドカードを指定する方法はありますか?

例えば:

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

*には有効なURL文字列を指定できます。または、/groupsに一致し、ハンドラー(peopleInGroupHandler)func内から残りを見つける唯一のソリューションですか?

61
Mat Ryer

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)
}
83
Evan Shaw

2011年以降、(2014+)他のソリューションを見つけることができます。
たとえば、 Gorilla Webツールキットのmuxパッケージ は、あらゆる種類のルーティングオプションを提供します。

  • オプションの正規表現を使用した、リクエストパスのパターンマッチング。
  • URLホストとスキーム、リクエストメソッド、ヘッダー、クエリ値のマッチング。
  • カスタム関数に基づいたマッチング。
  • ネストされたルーティングを簡単にするためのサブルーターの使用。

BYOR(Bring your own Router)httpライブラリ、 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 "、および ここでテスト済み
アイデアは、名前付き変数を抽出し、一致する正規表現をアセンブルし、「リバース」テンプレートを作成してURLを構築し、正規表現をコンパイルしてURL構築で使用される変数値を検証します。

54
VonC

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

6
Daveman

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/

1
nbari

@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
caike