Go の net/http で Vue.js / Angular 1 などの HTML5 history mode に対応する
type html5Handler struct { fs http.FileSystem routes []staticRoute } // Look up path when Vue.js HTML5 history mode is enabled // https://router.vuejs.org/ja/essentials/history-mode.html func enableHTML5Mode(fs http.FileSystem, routes []staticRoute) http.FileSystem { return &html5Handler{fs, routes} } func (handler *html5Handler) Open(p string) (http.File, error) { file, err := handler.fs.Open(p) if err != nil { return searchHTML5Mode(handler, p, err) } return file, err } // * Try again after removing prefix if it has // * Returns index.html otherwise func searchHTML5Mode(handler *html5Handler, p string, err error) (http.File, error) { p = path.Clean(p) for _, route := range handler.routes { if strings.HasPrefix(p, route.basePath) { file, err := handler.fs.Open(strings.TrimPrefix(p, route.basePath)) if err == nil { return file, nil } } if route.indexPattern.MatchString(p) { return handler.fs.Open("/index.html") } } return nil, err }
で
http.Handle("/", http.FileServer(http.Dir("assets/site")))
とかやってたところを
routes := []staticRoute{ staticRoute{indexPattern: regexp.MustCompile(`^/feeds/[0-9]+$`), basePath: `/feeds`}, } http.Handle("/", http.FileServer(enableHTML5Mode(http.Dir("assets/site"), routes)))
に変える。
そうすると routes
に定義したパス(この場合は /feeds/:id
)に GET でアクセスした場合にも index.html が返り、
index.html の <script>
タグや <img>
タグ等で指定した静的ファイルも(basePath
が取り除かれて再検索されるので)返るようになる。
もちろんもともと http.Dir()
で指定してたパスの階層とマッチする場合は今まで通り問題なく静的ファイルが返される。
今回やったのは func (FileSystem) Open(p string) (http.File, error)
で第2返り値の error が non-nil だった場合に指定された routes
で再検索してるだけです。
ちなみに path.Clean(p)
はディレクトリトラバーサル対策です。便利。
合わせて読みたい
open-browser.vim で Windows Subsystem for Linux 上の Vim から URL 開けるようになった
これで WSL 上であろうとなかろうと意識せず URL を開けるはずです。 最近は WSL の Vim ばっかり使ってるので個人的にうれしい機能追加です(追加したの自分だけど)。
WSL は Windows の世界と分断されてると思い込んでたのですが、
/mnt
で Windows のファイルシステムを触れるのは知ってたものの、
Windows の実行ファイル(PE フォーマットの実行ファイル)も起動できるのは知りませんでした。
bash から /mnt/c/Windows/System32/notepad.exe
とか開けるんですね。
というわけで直接 Windows で動いてる Vim *1 でも叩いてる rundll32.exe を直接叩いて URL を渡すだけでした。あっけない。
*1:この表現は誤解を招くような… WSL も同じサブシステム上で動いてるし。なんて言えばいいんだ?
Go のバージョンマネージャー gvm で gvm install したら「ERROR: Failed to compile」って言われた時
issue には上げたけど日本語でも書く。 結果から言うと自分が README.md よく読んでなかっただけ。 でも gvm ももうちょっと気を利かせてくれてもいいのになーと思ったので上の issue でお願いしたという経緯です。
解決法
Go 1.5 以上をコンパイルする場合は GOROOT_BOOTSTRAP
って環境変数を定義する必要があります。
これは .bash_profile
かなんかで
export GOROOT_BOOTSTRAP=$GOROOT
して gvm install すればいいのですが、「GOROOT
?そんなん定義したことないんだけど…」って人もいるかと思います
(現に今は GOROOT
環境変数は定義する必要はないらしいです)。
GOROOT
は Java にとっての JAVA_HOME
みたいなもので、go
コマンド等のベースパスを指定するものです。
で、じゃあどうすればいいのかというと、go
コマンド自体に聞いてしまえばいいとのこと。
export GOROOT_BOOTSTRAP=$(go env GOROOT)
これで GOROOT_BOOTSTRAP
には go
コマンド等のベースパスが入ります。
もちろん go
コマンドに PATH
が通ってないとダメなので後述するように先に PATH
を設定してやります。
どう設定すればいいか
自分の Go 関連の .bash_profile
を載せておきます(今回の話に関係ないのもありますが)。
# Go 1.5 以下で vendoring が有効になる # http://yoru9zine.hatenablog.com/entry/2016/02/02/054922 export GO15VENDOREXPERIMENT=1 if true; then # gvm の go コマンドを使う [[ -s "/home/tyru/.gvm/scripts/gvm" ]] && source "/home/tyru/.gvm/scripts/gvm" export GOROOT_BOOTSTRAP=$(go env GOROOT) else # system 標準の go コマンドを使う export GOPATH="$HOME/go" export PATH="$GOPATH/bin:$PATH" fi
true, false で gvm の Go か system の Go かを切り替えられるようにしてあります。