読者です 読者をやめる 読者になる 読者になる

goji.ioで正規表現Pattern

ハンドラのとこはgoji.ioで書いてます。見ると標準のnet/httpとgolang.org/x/net/contextをうまいこと活かしたからこその極薄なライブラリなのですが、リクエストパスでハンドラを切り替えるのに簡潔利便なので私はお好みです。しかし薄すぎて足りないところもあったのでパス切り替え判断のところ、ちょこっと補ってみました。

package patx

import (
    "regexp"
    "net/http"
    "golang.org/x/net/context"
)

type RegexpPattern struct {
    regexp *regexp.Regexp
}

func Regexp(expr string) *RegexpPattern {
    // MustCompileだと渡された表現文字列がコンパイルエラーのときpanicで止める。よろしい。
    return &RegexpPattern{regexp: regexp.MustCompile(expr)}
}

// goji.Patternインターフェイスを実装している
func (reg *RegexpPattern) Match(ctx context.Context, r *http.Request) context.Context {
    if reg.regexp.MatchString(r.URL.Path) {
        // 手抜きでMatchStringでの当り判定だけなので、積むものがなく、よってctxに何も積んでない。
        return ctx;
    }
    return nil
}

たったのこれだけで、とてつもなく手がかかってない実装ですが、そうはどっこい意外に便利。正規表現で書きたいこともある。

package main

import (
    "book"
    "goji.io"
    "goji.io/pat"
    "net/http"
    "patx"
)

func main() {
    mux := goji.NewMux()
    // 拡張子6種を簡単な正規表現でマッチさせてhttp.FileServerへルーティングする
    mux.Handle(
        patx.Regexp(`(.*\.(css|ttf|gif|png|jpg|js))$`),
        http.FileServer(http.Dir("public"))
    )
    mux.HandleFuncC(pat.Get("/book/list"), book.ListBookJSON)
    mux.HandleFuncC(pat.Get("/book/:title"), book.ListPageJSON)
    // こっから先はGoは静的HTML出すだけで、reactアプリになる
    mux.HandleFuncC(pat.Get("/*"), book.IndexHTML)
    http.ListenAndServe("localhost:8080", mux)
}

こんな感じです。GAEだと、こんな取り込みはいらず、yamlファイルに正規表現書いて同じことができますけど、平場のGOだと自分で書く。

なんで平場のGoで書かないといけないのか

GAE/GoのSDKでSierra対応版がまだ出てこないから。仮想でやるのは重いので緊急避難。GAE機能つかってるところはダメでも、そこらへんだけ殺してモック的に動かしておいて、当面はクライアントのところだけを書いてましょう。

後に気がつきましたが、macOS 10.12.1+go1.7.3 darwin/amd64環境下でGoのデバッガが「could not launch process: could not get thread count」と吐いてクラッシュしてしまいます。インクリメンタルデバッグはできない。実行はできるので、その延長線にてユニットテストはできる。

Goの正規表現

Syntax · google/re2 Wiki · GitHub

こちらの仕様とのことらしく。named captureができないなーと思ってたら、?P<name>が正解で?<name>は非サポートだった。。。