はじめに
HTMLでフォームを作るのは技術的には簡単なのですが、何度もやることになると作業の重複が多くなって疲れるかと思います。ここではProtocol Buffersを用いてフォームの表示と処理の自動化を試みてみます。実運用するには足りない点も多々ありますので、一つの実験として読んでみて頂ければと思います。
コードは https://github.com/yt76/pbforms にあるので、cloneして(もしくはcloneしたつもりになって)お読みください。
Protocol Buffersによるフォームの定義
例えば、名前(string)と年齢(int32)と同意の有無(bool)を取得したいとします。このメッセージを my_form.proto というファイルにあるように次の内容で定義します。
message MyForm {
optional string name = 1;
optional int32 age = 2;
optional bool agree = 3;
}
これをコンパイルし、
$ protoc myform.proto --go_out=forms/
それを使うサーバーをビルドし
$ go build server/server.go
実行し
$ ./server
Serving at 8080
ブラウザでアクセスすると
適当に埋めて
送信を押すと
内容がProtocol Buffersのテキスト形式で表示されます。実際のプログラム上ではこの各フィールドの値をデータベースに詰めることになりそうですが、このテキスト形式もしくはProtocol Buffersのバイナリ形式でそのまま保存したりRPCに投げることでProtocol Buffersの機能を使ったデータ操作をするなんてのも可能かと思います。
実装
実装といっても大したことはしてなくて、Goのリフレクションで各フィールドの名前と型を取り出してそれに応じたHTMLを生成し、POSTのハンドラ内ではフィールドの値を戻すといった感じです。
// コンストラクタ内
form := forms.MyForm{}
sv.writer = pbforms.NewFormWriter(form)
sv.reader = pbforms.NewFormWrite()
// GETのハンドラ
fmt.Fprintf(w, "<html><body>\n")
// <form method="post" action="/">…</form>が生成される
sv.writer.Write("/", w)
fmt.Fprintf(w, "</body></html>\n")
// POSTのハンドラ
f := forms.MyForm{}
// r *http.Requestでformの値からfを埋める
sv.reader.Parse(r, &f)
最後に
- すぐに思いつく改善点としては見栄えの改善ですが、適当なCSSのクラスを出力してあとからJavaScriptで外側のHTMLのスタイルに書き換えるという手でなんとかなりそうです。
- ここではstringとintとboolをHTML formのtextとcheckboxに対応させましたが、その他の要素もProtocol Buffersの様々な機能を使えばそんなに無理せずに対応できる?
- フォーム上にNameとかAgeとかフィールド名をそのまま出してしまってるので、別途メッセージカタログ(国際化もできればなお良い)的なものが必要ですね。
ネット上には様々なフォームがあって、その中には技術の粋をつくした一品物から何かのコピペで済ましたい安易なものまで色々とありますが、適当なレベルのところまではこの記事で紹介したようなものを多少改善すれば間に合うのではないかなと考えてます。