今後のことを考え、PHPでの開発はSOY Shopを動かす時ぐらいにして、

主力はGolangに移していきたいなとコードを書いていたところ、

Golangで立てたWebサーバでのセッションの取り扱いで苦戦した。


セッションとかはさすがPHP!

Webアプリ専用の言語だけあってよくできてる。


前置きはここまでにしておいて、

今回Golangで苦戦した箇所をメモとして残しておく




Golangの現在のバージョン(1.5.2)では、

標準でセッションを扱うためのライブラリはない。


そこで、

Gorilla web toolkitのsessionsパッケージを利用することにした。


はじめにGolang標準のテンプレートエンジンを利用する為に、

下記の様にPageの構造体を用意


type Page struct {
Name string
Email string
}

これを作っておくことで、いろいろと便利なことがあるんだけど置いといて


とあるページでフォームを表示出来るようにハンドラ用の関数を用意


import (
"html"
"html/template"
"net/http"

"github.com/gorilla/sessions"
)

func Page1(w http.ResponseWriter, r *http.Request) {
page := Page{}

t, _ := template.ParseFiles("register.html")
t.Execute(w, page)
}

register.htmlにはフォームのHTMLを記述して、

実行してみると


golang_form


こんな感じ。


confirmを押すと、確認画面を表示して、


golang_confirm


表示する直前にセッションに入力した値を入れる仕様にしておく。


確認ページの関数は下記の通り


var store = sessions.NewCookieStore([]byte("random-string"))

func Page2(w http.ResponseWriter, r *http.Request) {
r.ParseForm()

//戻るボタンを押した
if backBtn := r.FormValue("back"); len(backBtn) > 0 {

//トップページへ戻る
http.Redirect(w, r, "/", http.StatusFound)
return
} else {
name := html.EscapeString(r.FormValue("name"))
email := html.EscapeString(r.FormValue("email"))

//セッションに値を入れる処理
session, _ := store.Get(r, "goapp-p-register")
session.Values["name"] = name
session.Values["email"] = email
session.Save(r, w)

//ここからページの表示に関する処理
page := Page{Name:name, Email:email}
t, err := template.ParseFiles("confirm.html")
if err != nil {
panic(err)
}

err = t.Execute(w, page) // テンプレートをジェネレート
if err != nil {
panic(err)
}
}
}

Page2からbackボタンでPage1に戻った時に、

入力しておいた値をフォームに入れておきたいわけで、


func Page1(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "goapp-p-register")
name := session.Values["name"]
email := session.Values["email"]

page := Page{Name: name, Email: email}

t, _ := template.ParseFiles("register.html")
t.Execute(w, page)
}

上記の様にページを表示する前にセッションに入れた値を取得して、フォームに値を入れる様に書き換えた後、このコードを実行してみたところ


page := Page{Name: name, Email: email}

の箇所で、


main.go:28: cannot use name (type interface {}) as type string in field value: need type assertion

というエラーが表示された。



nameの型がinterface{}だって?


とりあえず、


name := session.Values["name"]
log.Printf("#v", name)

のようにしてセッションの中から取り出した値を見てみると、


#v%!(EXTRA string=齋藤 毅)

name変数の型はstringになってるぞ。


エラーメッセージはstringにアサーションをしてみろとのことだったので、


name := session.Values["name"]
if _, ok := name.(string); !ok{
log.Printf("#v", ok)
}

をしてみたら、


#v%!(EXTRA bool=true)

が返ってきたのに、アサーションしたものを構造体に入れてもダメだった。


どうすれば良いのか?といろいろと考えてみた結果、


type Page struct {
Name interface{}
Email interface{}
}

にしてみたら、うまくいった。


構造体に値を突っ込むとき、

構造体の方では静的な型を見るけど、


name := session.Values["name"]

では、動的に型を定義するみたいで、

構造体に突っ込むときに動的に定義した型の値ではダメみたいだ。


何か良い方法はないかな?