今後のことを考え、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を記述して、

実行してみると



こんな感じ。


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"]

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

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


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