Method as Argument in Go

說來慚愧,這麼好用的作法竟然今天才發現。

一直以來都覺得 net/http 有點難用,http.Handlehttp.Handler,定義是

1
2
3
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}

所以非得有個 method ServeHTTP,但如果要在一個 Handler 底下有好幾個 method 用以處理各類 request,進入點都是 ServeHTTP 就有點麻煩。這時用 http.HandleFunc 就比較合理,只要傳入一個 function:

1
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))

然而,這類函數經常需要 context 才能執行,例如把資料庫連結存在 type 裡。 以往碰到這情況會利用 closure 的特性,在 method 中宣告符合介面的函數,然 後再傳給 HandleFunc,現在才知這完全是不必要的。舉例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package test

import (
"log"
"net/http"
)

type Model struct {
// Your database connection, etc.
}

func (t Model) Foo(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("Foo"))
}

func (t Model) Bar(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("Bar"))
}

func Example() {
model := Model{}
http.HandleFunc("/foo", model.Foo) // method as argument
http.HandleFunc("/bar", model.Bar)
log.Fatal(http.ListenAndServe(":8080", nil))
}

實在是蠻漂亮的作法,搭配 Gorilla web toolkit 很容易就可以寫出 簡單明瞭的 server side 程式。