GoのWeb Application Framework, ミドルウェア, Contextについて

Go1.7でContextが追加されますが、その使いどころや、GoでのWeb開発にはたしてミドルウェアスタックや3rd party Web Application Frameworkは必要なのか、などについて @sona_tar さんとディスカッションをしました。 サンプルコード多めなので、そちらも見つつ追いかけてみてください。
1
sonata@private @sona_tar

Golangの標準インターフェースだけを使ってMiddleware間の値の受け渡しができた go1.7のr.Context(), r.WithContext(), context.WithValue(), ctx.Value()を使う

2016-07-08 18:07:05
sonata@private @sona_tar

やっとcontext周り理解して改めて調べてみるとbradfitz自身はあまりmiddleware間のパラメータ渡しに関して標準ライブラリに機能追加するつもりはなさそう あとWithContextはやっぱりgoroutine以外で使うべきではないかな

2016-07-09 13:53:25
小野マトペ @ono_matope

@sona_tar Contextはタイムアウトとキャンセルの伝播のために使うに止めて、そもそもパラメータ渡しが必要なMiddlewareは使わない(net/http+アプリケーションごと独自Contextで充分)というスタンスに落ち着きました。

2016-07-09 14:01:08
sonata@private @sona_tar

@ono_matope やはりそうするしかないんですねー こんな感じでしょうか? play.golang.org/p/eM0Ba4yaeD 3rd partyのフレームワークのように大幅にHandleが標準フレームワークから外れたりグローバルスコープにContextを持ちたくない、続く

2016-07-09 14:49:06
sonata@private @sona_tar

@ono_matope Go1.7からContextが標準になる、RequestにWithContextが追加される、内部で同名で別目的のContextを追加で定義したくない、など色々ありどうにかして標準のContextを使えないか調べていたのですが無理でしたw

2016-07-09 14:49:18
sonata@private @sona_tar

@ono_matope こうなるとmiddleware間でパラメータを渡す手段としてContextという名前を使うのはやめたほうがいいと思えてきますね 標準のContextは1つのリクエスト内の複数のgoroutineを同一スコープとして表現するために存在すると思うので

2016-07-09 14:55:05
小野マトペ @ono_matope

@sona_tar 僕はむしろMiddleware stacing(単機能のハンドラをラップしてくやつ)に対して懐疑的で、全部一つのハンドラ内でやっちゃえばやりとりも楽でいいじゃんという考えですねー → play.golang.org/p/IevdwwgV5d

2016-07-09 15:06:37
小野マトペ @ono_matope

play.golang.org/p/IevdwwgV5d この例大事なところが書き落とされてるので少し修正します play.golang.org/p/QVEZXRQ6wl

2016-07-17 15:48:31
sonata@private @sona_tar

@ono_matope なるほど! そちらのほうが手続きが明示されるので素直ですね RouteActionではgolang.org/src/net/http/s…みたいにRequestのurlを参照してactionを返す処理を独自実装するのでしょうか?

2016-07-09 15:19:35
小野マトペ @ono_matope

@sona_tar ですです。自前でrouterを実装するか、中でhttprouterなどを利用してAction関数またはオブジェクトを返してます。WAFに付属のルータって狙ったURLでのルーティングができなかったりすることが多いのもWAF使わない理由の一つですね。

2016-07-09 15:23:55
sonata@private @sona_tar

@ono_matope httprouterというパッケージもあるんですね 先ほどのスニペットとの違いとしてはセットアップ時にHandleをセットして確定させるか、リクエスト来た後にハンドルを判断するかという感じですね

2016-07-09 15:32:32
sonata@private @sona_tar

@ono_matope 自分もWAFは使いたくないのですが、gorillaのような標準に+αが前提のパッケージ群はいいなと思います これだと最悪必要なところだけ参考にして独自実装で逃げれます といってもこの特性のために気軽にグローバルスコープを使うので採用するかはまた別問題ですが

2016-07-09 15:43:12
小野マトペ @ono_matope

@sona_tar httprouterは高速なURLマッチ実装として使ってる感じですね。gorillaはユーティリティとして使えて僕の書き方とも相性が良いので、たまに使ってます

2016-07-09 15:47:10
sonata@private @sona_tar

@ono_matope httprouterの実装勉強してみます 色々と有難うございました! すっきりしたのでやっと手を動かせそうです

2016-07-09 15:53:32
sonata@private @sona_tar

httprouter標準で第3引数にparamがある! あとmethodごとにhandler登録できるのも欲しかった機能 middlewareだけを独自実装すれば良さそう github.com/julienschmidt/…

2016-07-09 16:00:15
sonata@private @sona_tar

bradfitzの見解を知りたい WithContext実装される際に彼が指揮を取っていたから想定した使われ方は彼が知っているはずなんだけど twitter.com/sona_tar/statu…

2016-07-17 09:10:20
sonata@private @sona_tar

色々な意見あるな その1. contextは3rd partyのlibraryを経由するときにだけ使うべき そもそもmiddlewareはオーバーヘッドが大きいからアンチパターンだ 2. contextはmicroservicesのように他のサービスに値を渡すときに使う

2016-07-17 09:29:43
sonata@private @sona_tar

これだ! 3. Go1.7公式ドキュメントいわく、contextはリクエストスコープのデータを他のプロセスに移し替えるときに使う 関数へのパラメータを渡すためではない twitter.com/sona_tar/statu…

2016-07-17 09:32:53
sonata@private @sona_tar

とりあえずcontextをmiddleware間のパラメータ私に使うべきではないことがわかってよかった あとはmiddlewareの是非の議論が読みたい

2016-07-17 09:40:05
小野マトペ @ono_matope

"I think middleware is a huge anti-pattern in Go" そうそう。ほんとそう思う。middleware使わないのが正解。 reddit.com/r/golang/comme…

2016-07-17 00:37:08
sonata@private @sona_tar

@ono_matope play.golang.org/p/occbVix6ec 同じスレッドでその発言をしたdanredux氏が書いたコードですけどこれはmiddlewareとは違うということなのでしょうか?自分はmiddelwareに見えてしまいます 彼はpipelineと呼んでいる?

2016-07-17 09:46:44
小野マトペ @ono_matope

@sona_tar さわりしか読めてないですが、おそらく彼は(少なくとも僕は)「ログミドルウェア」「認証…」「gzip圧縮…」というように、単一機能をミドルウェア化してスタックするパターンをアンチパターンだとして批判してるのではないかと。どれもGoなら数行で書けますし。

2016-07-17 12:41:56
sonata@private @sona_tar

@ono_matope その中でページによって「認証必須で必ずユーザ情報を渡す」 「 認証していればユーザ情報を渡す」「ユーザ情報不要」という違いをこのように実現したいと考えています play.golang.org/p/_Dfqk-29DQ 続く

2016-07-17 13:48:35