htmlのtemplate【Go】 - 技術向上などで紹介した方法ですと、
各handlerで共通した、titleを取得する処理が行われています。
引数にtitleを渡すことで効率化できそうです。
...
func viewHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/view/"):]
...
}
func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/edit/"):]
...
}
func saveHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/save/"):]
...
}
func main() {
http.HandleFunc("/view/", viewHandler)
http.HandleFunc("/edit/", editHandler)
http.HandleFunc("/save/", saveHandler)
log.Fatalln(http.ListenAndServe(":8080", nil))
}
しかし、呼び出し元であるhttp.HandleFuncの第2引数には、
handler func(ResponseWriter, *Request)が指定されており、
呼び出し元でviewHandler(title)のようにhandler func(ResponseWriter, *Request)に対して新たな引数を指定することはできません。
そこで『「新たな引数を含む関数」を引数として、func(ResponseWriter, *Request)を返す関数』を作成し、
http.HandleFuncの第2引数に指定します。
ただし、「新たな引数」は呼び出し元では指定しません。titleはrequestのurlから取得できるからです。
...
func viewHandler(w http.ResponseWriter, r *http.Request, title string) { // 第3引数titleを指定
p, err := load(title)
if err != nil {
http.Redirect(w, r, "/edit/"+title, http.StatusFound)
return
}
renderTemplate(w, "view", p)
}
func editHandler(w http.ResponseWriter, r *http.Request, title string) {
p, err := load(title)
if err != nil {
p = &Page{Title: title}
}
renderTemplate(w, "edit", p)
}
func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
body := r.FormValue("body")
p := &Page{Title: title, Body: []byte(body)}
err := p.save()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/view/"+title, http.StatusFound)
}
var validPath = regexp.MustCompile("^/(view|edit|save)/([a-zA-Z0-9]+)$") // 正規表現をキャッシュ。「^」は先頭、「$」は末尾を意味する
func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc { // 3つの引数をもつfunctionを引数にとり、func(ResponseWriter, *Request)を返す
return func(w http.ResponseWriter, r *http.Request) {
m := validPath.FindStringSubmatch(r.URL.Path) // キャッシュした正規表現にマッチするpathを[]stringで格納
if m == nil {
http.NotFound(w, r) // マッチしない場合は、404エラーを返す
return
}
fn(w, r, m[2]) // 第3引数に[]string([全体、view|edit|save、title])の3番目titleを指定
}
}
func main() {
http.HandleFunc("/view/", makeHandler(viewHandler)) // makeHandlerを呼び出す
http.HandleFunc("/edit/", makeHandler(editHandler))
http.HandleFunc("/save/", makeHandler(saveHandler))
log.Fatalln(http.ListenAndServe(":8080", nil))
}
htmlファイルも含めた全体の構成はhttp.Redirect【Go】 - 技術向上を、
正規表現についてはregex(正規表現)【Go】 - 技術向上を参照ください。