Goのhttpルーター「Chi」の紹介

少し遅れてしまいましたが、Origami Advent Calendar 2016の21日目の記事になります。

OrigamiではGo言語をメインの言語として、APIなどのHTTPアプリケーションサーバを作製しています。

golangでHTTPアプリケーションを作成するときには
フレームワークを特に導入しなくとも、
`net/http`とそれに準拠したライブラリを組み合わせれば十分だと考えています。

基本的には`net/http`の機能の組み合わせで大抵の機能は十分表現できます。
ただし、ルーティングについては標準の`http.ServeMux`だとちょっとつらいことが多いと思います。
標準のAPIに準拠しつつ、実アプリケーションでも十分な機能を備えたルーティングライブラリはいくつかあります。

Origamiのいくつかのアプリケーションサーバでは、そのうちの[chi](https://github.com/pressly/chi)を利用しています。

# chi

https://github.com/pressly/chi

[chi](https://github.com/pressly/chi)は`net/http`の`http.Handler`インターフェースに準拠したルーティング・ライブラリです。

標準の`http.ServeMux`と比較して以下のような機能があります。

- ワイルドカード付きパターンのサポート
- 名前付きパラメータのサポート
- ミドルウェアのサポート

ルーティング・ライブラリとしての特色は他に以下があります。

- Go 1.7 で追加された`context`パッケージに準拠
- `net/http`のインターフェースに準拠
- Go 1.7と`net/http`にのみ依存
- パトリシア木による高速マッチ（httprouterと同じ）

golangの標準インターフェースに準拠しており、かつ、ある程度高速で柔軟なマッチングのできるルータがほしいなら、chiはかなりおすすめできるライブラリです。

実際、chiの採用事例としては以下のような企業があります。

- CloudFlare
- Heroku
- **Origami**

# chiの利用
chiの使い方は単純で、ルータを作成して`net/http`のサーバにマウントするだけです。

```go
package main

import (
	"net/http"
	"github.com/pressly/chi"
)

func main() {
	r := chi.NewRouter()
	http.ListenAndServe(":80", r)
}
```

ルータへのハンドラの追加は以下のように行います。

```go
// 標準のHandlerインターフェースを受け取ります.
var handler http.Handler = someHandler
r.Handle("/foo", handler)

// HandlerFunc型で受けることもできます.
var handlerF http.HandlerFunc = someHandler.ServeHTTP
r.HandleFunc("/bar", handlerF)

// 特定のメソッドのみ受け取るメソッドも用意されています.
// HTTPメソッドでのルーティングは、HandlerFuncを指定することに注意してください.
r.Get("/baz", handlerF)
```

ワイルドカードや名前付きパラメータを受け取ることもできます。

```go
r.Get("/users/:id/*", func(w http.ResponseWriter, r *http.Request) {
  userID := chi.URLParam(r, "id")
  // ...
})
```

サブルータを作ることもできます。

```go
subr := chi.NewRouter()
subr.Get("/bar", FooBarHandler)
r.Mount("/foo", subr)
// => /foo/bar へのリクエストは FooBarHandler に送られます.

// サブルータは以下のように作成することもできます.
r.Route("/foo", func(subr chi.Router){
  subr.Get("/bar", FooBarHandler)
})
```

# Middleware

chiは`middleware`パターンによるミドルウェア（プラグイン）を利用することができます。
`middleware`パターンとは以下のように`http.Handler`をラップする関数を指します。

```go
// http.Handlerを受け取って、前後に何らかの処理を足し、
// ラップした新しいhttp.Handlerを返すような関数を
// middlewareと呼びます.
func middleware(next http.Handler) http.Handler {
  // ※http.HandlerFunc型の関数はhttp.Handlerインターフェースを持ちます.
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    // 前処理
    next(w, r)
    // 後処理
  })
}

```

`middleware`関数を通すことで様々な共通処理をハンドラに追加することができます。

```go
var newHandler1 = middleware(originalHandler1)
var newHandler2 = middleware(originalHandler2)
// ラップしたハンドラをマウント
// newHandler1, newHandler2はそれぞれmiddlewareに実装された処理を共通して持つ.
r.Get(pattern1, newHandler1)
r.Get(pattern2, newHandler2)
```

chiではこのような`middleware`関数の適用を自動で行ってくれます。

```go
// middleware1は全体に適用されます
r.Use(middleware1)
r.Get("/foo", handler1)

// サブルータを作成してミドルウェアを追加することもできます.
// サブルータに適用されたミドルウェアはサブルータのみ有効です.
r.Route("/bar", func(subr chi.Router) {
  subr.Use(middleware2)
  subr.Get("/baz", handler2)
})

// 適用されるミドルウェア
// /foo => middleware1
// /bar/baz => middleware1, middleware2
```

chiでは上記の`middleware`パターンを適用したミドルウェアがいくつか用意されいて、
`chi/middleware`サブパッケージにまとめられています。

例えば`middleware.RequestID`はRequestIDをそれぞれのリクエストにセットします（`context`経由で取り出せます）。
`middleware.Timeout`はタイムアウト機能を提供します。
他にも便利なパッケージがあるので自分で実装する前にこれらのパッケージを利用してもよいでしょう。

# おわりに
`chi`はシンプルですが非常に有用なルーティングライブラリです。

Goでは巨大なライブラリやフレームワークを使うより、
こういったシンプルなコンポーネントを組み合わせていく方が、
より「Goらしい」スタイルではないかと思っています。

その意味で`net/http`と組み合わせやすい`chi`は非常に取り回しがよく、
採用して安心感が持てました。