golangでjsonのデータを出力する
一気に気温が上がったり、下がったり大変
安定せんかな〜
実装1(シンプルな構造体)
main1.go
package main import ( "fmt" "net/http" "log" "github.com/gorilla/mux" "encoding/json" ) // 単純な構造体 type Data1 struct { Title string `json:"title"` Message string `json:"message"` Status int `json:"status"` } func main(){ // ルーティング router := mux.NewRouter() // URL別設定 router.HandleFunc("/sample1", func(w http.ResponseWriter, r *http.Request){ // 構造体を定義 var data1 = Data1{} data1.Title = "sample1" data1.Message = "hello, sample1" data1.Status = 100 // jsonエンコード outputJson, err := json.Marshal(&data1) if err != nil { panic(err) } // jsonヘッダーを出力 w.Header().Set("Content-Type", "application/json") // jsonデータを出力 fmt.Fprint(w, string(outputJson)) // log log.Print(r.URL.Path) }) // ハンドル割当 http.Handle("/", router) // log log.Print("localhost:9001") // ポート http.ListenAndServe(":9001", nil) }
↓
実装2(構造体に配列を含む)
main2.go
package main import ( "fmt" "net/http" "log" "github.com/gorilla/mux" "encoding/json" ) // 配列を含む構造体 type Data2 struct { Title string `json:"title"` Message string `json:"message"` Status int `json:"status"` List map[string]Item `json:"list"` } // 構造体の配列Item type Item struct { Name string `json:"name"` Age int `json:"age"` } func main(){ // ルーティング router := mux.NewRouter() // URL別設定 router.HandleFunc("/sample2", func(w http.ResponseWriter, r *http.Request){ // 構造体を定義 var data2 = Data2{} data2.Title = "sample2" data2.Message = "hello, sample2" data2.Status = 200 data2.List = map[string]Item{} data2.List["test1"] = Item{Name:"taro", Age: 10} data2.List["test2"] = Item{Name:"hanako", Age: 18} data2.List["test3"] = Item{Name:"pochi", Age: 50} // jsonエンコード outputJson, err := json.Marshal(&data2) if err != nil { panic(err) } // jsonヘッダーを出力 w.Header().Set("Content-Type", "application/json") // jsonデータを出力 fmt.Fprint(w, string(outputJson)) // log log.Print(r.URL.Path) }) // ハンドル割当 http.Handle("/", router) // log log.Print("localhost:9001") // ポート http.ListenAndServe(":9001", nil) }
↓
なんとかできました。
RSSの場合にもあったけど
type Data2 struct { Title string `json:"title"` Message string `json:"message"` Status int `json:"status"` List map[string]Item `json:"list"` }
右の方にjsonとか指定したらその名前になるので小文字にしたい場合などは役に立ちます…というかきまりみたいな感じかな…
テキトーにググったけどそれっぽいパッケージが見つからなかったのでこれでいいはず
golangでRSS2のデータを取得して表示
参考
[Golang] XML Parsing Example (7) - Parse RSS 2.0
※Atomや両方対応のパターンもあります
静的ファイルも参照したい
jsとかcssとか使用したいけどどうやって参照するか
web applications - Serving static content with a root URL with the Gorilla toolkit - Stack Overflow
実装
構成
. ├── main.go ├── static │ ├── css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap-theme.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ └── js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── jquery-2.2.0.min.js │ ├── jquery-2.2.0.min.map │ └── npm.js └── templates └── index.html
main.go
package main // インポート import ( "log" "net/http" "html/template" "github.com/gorilla/mux" "io/ioutil" "encoding/xml" ) // RSS2用の構造体 type Rss2 struct { XMLName xml.Name `xml:"rss"` Version string `xml:"version,attr"` // Required Title string `xml:"channel>title"` Link string `xml:"channel>link"` Description string `xml:"channel>description"` // Optional PubDate string `xml:"channel>pubDate"` ItemList []Item `xml:"channel>item"` } // RSS2の各記事の部分(Item) type Item struct { // Required Title string `xml:"title"` Link string `xml:"link"` Description template.HTML `xml:"description"` // Optional Content template.HTML `xml:"encoded"` PubDate string `xml:"pubDate"` Comments string `xml:"comments"` } // 主処理 func main(){ // ルーティング初期化 r := mux.NewRouter() // 静的ファイルをサポート r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static")))) // url別処理 r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){ // httpよりRSS取得 _rssURL := "http://m-shige1979.hatenablog.com/rss" res, err := http.Get(_rssURL) if err != nil { panic(err) } // text形式で読み込む asText, err2 := ioutil.ReadAll(res.Body) if err2 != nil { panic(err2) } // rss2構造体を定義 rssData := Rss2{} // パースして格納 err3 := xml.Unmarshal(asText, &rssData) if err3 != nil { panic(err3) } // テンプレート生成 tmpl := template.Must(template.New("index").ParseFiles("templates/index.html")) // 変数を設定 tmpl.Execute(w, struct{ RssItemList []Item }{ RssItemList: rssData.ItemList, }) // url log.Println(r.URL.Path) }) // ルーティング設定 http.Handle("/", r) // ポート待ち log.Println(":9001") http.ListenAndServe(":9001", nil) }
templates/index.html
{{ define "index" }} <!DOCType html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>websample3</title> <link rel="stylesheet" href="/static/css/bootstrap.min.css" /> </head> <body> <div class="container-fluid"> <div class="row"> <div class="col-xs-12"> <h3>RSSリスト</h3> </div> </div> <div class="row"> <div class="col-xs-12"> {{ range .RssItemList }} <div> <blockquote> <p>{{ .PubDate }}</p> <p>{{ .Title }}</p> <footer> <a href="{{ .Link }}" target="_blank">{{ .Link }}</a> </footer> </blockquote> </div> {{ end }} </div> </div> </div> <script src="/static/js/jquery-2.2.0.min.js"></script> <script src="/static/js/bootstrap.min.js"></script> </body> </html> {{ end }}
結果
golangでgorillaとかいうのを使ってみる(sessions)
セッションを使用してみる
値を入れること自体はそんなに難しく無い感じですけど取り出すときになんかいろいろ手間取る感じ
基本的には構造体などで管理したほうがいい感じです
インストール
go get github.com/gorilla/sessions
実装
main.go
package main import ( "encoding/gob" "github.com/gorilla/mux" "github.com/gorilla/sessions" "net/http" "fmt" "time" "html/template" "crypto/rand" "encoding/base32" "io" "strings" ) // セッション名 var session_name string = "gsid" // Cookie型のstore情報 var store *sessions.CookieStore // セッションオブジェクト var session *sessions.Session // 構造体 type Data1 struct { Count int Msg string } // 主処理 func main(){ // 構造体を登録 gob.Register(&Data1{}) // セッション初期処理 sessionInit() // ルーティング生成 r := mux.NewRouter() // URL別の処理 r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){ // セッションオブジェクトを取得 session, _ := store.Get(r, session_name) data1, ok := session.Values["data1"].(*Data1) if data1 != nil { data1.Count++ data1.Msg = fmt.Sprintf("%d件カウント", data1.Count) } else { data1 = &Data1{0, "データ無し"} } fmt.Println(ok) fmt.Println(data1) session.Values["data1"] = data1 // 保存 sessions.Save(r, w) // テンプレートを指定 tmpl := template.Must(template.New("index").ParseFiles("templates/index.html")) tmpl.Execute(w, struct { Detail *Data1 }{ Detail: data1, }) // logの代わり fmt.Print(time.Now()) fmt.Println(" url = " + r.URL.Path) }) r.HandleFunc("/clear", func(w http.ResponseWriter, r *http.Request){ // セッション初期化 sessionInit() // logの代わり fmt.Print(time.Now()) fmt.Println(" url = " + r.URL.Path) // redirect http.Redirect(w, r, "/", http.StatusFound) }) // rを割当 http.Handle("/", r) // ポートを割当 fmt.Println("localhost:9001") http.ListenAndServe(":9001", nil) } // セッション用の初期処理 func sessionInit(){ // 乱数生成 b := make([]byte, 48) _, err := io.ReadFull(rand.Reader, b) if err != nil { panic(err) } str := strings.TrimRight(base32.StdEncoding.EncodeToString(b), "=") // 新しいstoreとセッションを準備 store = sessions.NewCookieStore([]byte(str)) session = sessions.NewSession(store, session_name) // セッションの有効範囲を指定 store.Options = &sessions.Options{ Domain: "localhost", Path: "/", MaxAge: 0, Secure: false, HttpOnly: true, } // log fmt.Println("key data --") fmt.Println(str) fmt.Println("") fmt.Println("store data --") fmt.Println(store) fmt.Println("") fmt.Println("session data --") fmt.Println(session) fmt.Println("") }
templates/index.html
{{ define "index" }} <!DOCType html> <html> <head> <meta charset="utf-8" /> <title>websample2</title> </head> <body> count = {{ .Detail.Count }}<br /> msg = {{ .Detail.Msg }}<br /> <br /> <a href="/clear">クリア<a/> </body> </html> {{ end }}
セッションのデータ管理は?
store = sessions.NewCookieStore([]byte(str))
これで"store"とかいう変数にCookie形式の形っぽいデータをどっかに管理するみたい
ちなみにローカルファイル内に配置したい場合は
store = sessions.NewFilesystemStore("", []byte(str))
こうなる
第1引数はセッションファイルの配置場所で空文字の場合は規定の場所を指定centosとかでは"/tmp"みたい…
byteで渡す値でデータを管理しているのでデータを初期化するという意味では一度別の名前にするのが良いかと
Valuesの値を消したりしてもいいかもしれないけど面倒な感じもあるので乱数を生成してから消すのも手かと思われる
所感
なんか遅い…
手動でログを出力しているせいかもしれないけどなんか応答が結構遅い感じがする
ルーティングとセッションをとりあえずだけど対応したのでちょっと他のこともやってみる
うーんやることが増えてきたのでソースがでかくなってきた\(^o^)/
golangでgorillaとかいうのを使ってみる(mux)
ゴリラ?
Gorilla, the golang web toolkit
まあ、http関連のやつでルーティングとかセッションとかを"net/http"よりは柔軟に使える感じのもの
なんか適当に調べたけど"net/http"ではセッションは使えないとかなんとか
ちょっと今回はルーティングの"gorilla/mux"を使用する
インストール
go get github.com/gorilla/mux
実装
今回の構成
. ├── main.go └── templates ├── index.html └── save.html
main.go
package main import ( "net/http" "github.com/gorilla/mux" "fmt" "time" "html/template" ) func main(){ // 生成 r := mux.NewRouter() // "/"の場合の処理 r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { // テンプレート tmpl := template.Must(template.New("index").ParseFiles("templates/index.html")) err := tmpl.Execute(w, nil) if err != nil { panic(err) } // logの代わり fmt.Print(time.Now()) fmt.Printf(" path: " + r.URL.Path + "\n") }).Methods("GET") // "/save"の場合の処理 r.HandleFunc("/save", func(w http.ResponseWriter, r *http.Request) { // formの値を取得 r.ParseForm() data1 := r.FormValue("aaa") data2 := r.FormValue("bbb") // テンプレート tmpl := template.Must(template.New("save").ParseFiles("templates/save.html")) err := tmpl.Execute(w, struct { Data1 string Data2 string }{ Data1: data1, Data2: data2, }) if err != nil { panic(err) } // logの代わり fmt.Print(time.Now()) fmt.Printf(" path: " + r.URL.Path + "\n") }).Methods("POST") // 可変URL1 r.HandleFunc("/test1/{sample1}/", func(w http.ResponseWriter, r *http.Request) { // 画面サンプル fmt.Fprintf(w, "sample1\n") // logの代わり fmt.Print(time.Now()) fmt.Printf(" path: " + r.URL.Path + "\n") }) // 可変URL2 r.HandleFunc("/test2/{sample1}/{id:[0-9]{1,6}}/", func(w http.ResponseWriter, r *http.Request) { // 画面サンプル fmt.Fprintf(w, "sample2\n") // logの代わり fmt.Print(time.Now()) fmt.Printf(" path: " + r.URL.Path + "\n") }) // handing http.Handle("/", r) // 設定 http.ListenAndServe(":9001", nil) }
※POST、GETだけを指定したい場合など、また一部のURLが可変になる場合は楽できます。
templates/index.html
{{ define "index" }} <!DOCType html> <html> <head> <meta charaset="utf-8" /> <title>websample1</title> </head> <body> <h3>なんかいれて送信してみて</h3> <form method="post" action="/save"> <input type="text" name="aaa" value="" /><br /> <input type="text" name="bbb" value="" /><br /> <input type="submit" value="送信" /> </form> </body> </html> {{ end }}
templates/save.html
{{ define "save" }} <!DOCType html> <html> <head> <meta charaset="utf-8" /> <title>websample1</title> </head> <body> <h3>送信されたものは以下</h3> <div> aaa = {{ .Data1 }}<br /> bbb = {{ .Data2 }}<br /> </div> <a href="/">top</a> </body> </html> {{ end }}
画面
↓
↓
所感
このツールキットを使用することでネットワーク関連のコードを書くのに結構便利になる感じかと
web アプリを作成するとしたらルーティングとセッションは必須なのできちんとやっておく必要がありそうです。
golangでtemplate(html)の確認
ページを表示する際はテンプレートを使用する
標準では"text/template"と"html/template"が存在する
※他にもあるかもしれないけどあとでやる
参考
Go言語でhttpサーバーを立ち上げてHello Worldをする - Qiita
template - The Go Programming Language
golang で html/template でのテンプレートの継承と、HTML エスケープしないで変数を出力する方法 (Django, Jinja みたいに) - Qiita
[Go言語] templateパッケージでtwigとかのextends的なあれをやるにはどうしようか - Qiita
シンプルなテンプレート
構造
$ tree . ├── main.go └── templates └── index.tpl $
templates/index.tpl
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>golang template test</title> </head> <body> <h1>Golang Template Sample</h1> <p>Title --[{{.Title}}]</p> <p>Message--[{{.Message}}] </p> <div> <h3>Items</h3> {{range .List}} <p>{{.}}</p> {{end}} </div> <div> Link: {{.Link}} </div> </body> </html>
※テンプレートは中括弧("{}")で挟むようです
main.go
package main import ( "fmt" "net/http" "html/template" "time" ) func main(){ http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Println(time.Now().Format("2006/01/02 15:04:05") + " " + r.URL.Path) // テンプレート用のファイルを読み込む tpl, err1 := template.ParseFiles("templates/index.tpl") if err1 != nil { panic(err1) } // テンプレートを出力 err2 := tpl.Execute(w, struct { Title string Message string List []string Link string }{ Title: "Hello", Message: "World", List: []string{ "Item1", "Item2", "Item3", }, Link: "<a href=\"/\">hoge</a>", }) if err2 != nil { panic(err2) } }) http.ListenAndServe(":9001", nil) }
ページをひらくとこうなる
一部のデータを加工する
templates/index.tpl
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>golang template test</title> </head> <body> <h1>Golang Template Sample</h1> <p>Title --[{{.Title}}]</p> <p>Message--[{{.Message}}] </p> <div> <h3>Items</h3> {{range .List}} <p>{{.}}</p> {{end}} </div> <div> Link: {{.Link | safe}} </div> <div> Link: {{.Link | lock}} </div> </body> </html>
main.go
package main import ( "fmt" "net/http" "html/template" "time" ) func main(){ http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Println(time.Now().Format("2006/01/02 15:04:05") + " " + r.URL.Path) // 独自関数でエスケープを解除 funcMap := template.FuncMap{ "safe": func(text string) template.HTML { return template.HTML(text) }, "lock": func(text string) string { return "@@[" + text + "]@@" }, } // テンプレート用のファイルを読み込む tpl, _:= template.New("index.tpl").Funcs(funcMap).ParseFiles("templates/index.tpl") // テンプレートを出力 err2 := tpl.Execute(w, struct { Title string Message string List []string Link string }{ Title: "Hello", Message: "World", List: []string{ "Item1", "Item2", "Item3", }, Link: "<a href=\"/\">hoge</a>", }) if err2 != nil { panic(err2) } }) http.ListenAndServe(":9001", nil) }
結果
複数のテンプレートをファイルを使用する
構成
. ├── main.go └── templates ├── base.tpl └── contents.tpl
templates/base.tpl
{{ define "base" }} <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>golang template test</title> </head> <body> <h1>Golang Template Sample</h1> <p>Title --[{{.Title}}]</p> <p>Message--[{{.Message}}] </p> <div> {{ template "contents" . }} </div> </body> </html> {{ end }}
templates/contents.tpl
{{ define "contents" }} <div> <h3>Items</h3> {{range .List}} <p>{{.}}</p> {{end}} </div> <div> Link: {{.Link | safe}} </div> <div> Link: {{.Link | lock}} </div> {{ end }}
main.go
package main import ( "fmt" "net/http" "html/template" "time" ) func main(){ http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Println(time.Now().Format("2006/01/02 15:04:05") + " " + r.URL.Path) // 独自関数でエスケープを解除 funcMap := template.FuncMap{ "safe": func(text string) template.HTML { return template.HTML(text) }, "lock": func(text string) string { return "@@[" + text + "]@@" }, } // テンプレート用のファイルを読み込む tpl := template.Must(template.New("hoge").Funcs(funcMap).ParseFiles("templates/base.tpl", "templates/contents.tpl")) // テンプレートを出力 err2 := tpl.ExecuteTemplate(w, "base", struct { Title string Message string List []string Link string }{ Title: "Hello", Message: "World", List: []string{ "Item1", "Item2", "Item3", }, Link: "<a href=\"/\">hoge</a>", }) if err2 != nil { panic(err2) } }) http.ListenAndServe(":9001", nil) }
頭パンクしそう(´・ω・`)
所感
やり方自体はパターンがあるからそのうち理解出来ていくと思うけどgo言語の書き方に慣れていないことや日本語のマニュアルがないのかエラーの意味をうまく理解できていないことがある。
いろいろ参考になりそうなソースがネットで検索したら出てくるので参考にしながらやっていくしかない
golangで超簡単なサーバ
実装
sample
package main import ( "fmt" "net/http" ) func main(){ http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){ fmt.Fprint(w, "Golang Http Server") }) http.ListenAndServe(":8080", nil) }
↓
こんだけ
所感
やり方はnodejsと同じイメージかな…
jsonとかを扱う方法とかもちょっと調べておく
golangの学習(time)
日時に関する情報を制御する
timeパッケージを使用する
参考
https://golang.org/pkg/time/#Now
http://ashitani.jp/golangtips/tips_time.html
http://tdoc.info/blog/2013/04/10/go_time.html
現在日時を取得する
sample
package main import ( "fmt" "time" ) func main(){ _now := time.Now() fmt.Println(_now) }
↓
$ go run time1.go 2016-01-19 22:46:07.181469119 +0900 JST $
日時を任意のフォーマットで表示する
https://golang.org/src/time/format.go
※フォーマットの指定値がややこしいので気をつけないといけない
sample
package main import ( "fmt" "time" ) func main(){ // 現在日時を取得 _now := time.Now() // format const YYYYMMDDHHMISS1 = "2006/01/02 15:04:05" const YYYYMMDDHHMISS2 = "06-1-2 3:4:5" fmt.Println(_now.Format(YYYYMMDDHHMISS1)) fmt.Println(_now.Format(YYYYMMDDHHMISS2)) fmt.Println(_now.Format(time.RFC3339)) }
↓
$ go run time2.go 2016/01/19 22:56:21 16-1-19 10:56:21 2016-01-19T22:56:21+09:00 $
Unixタイムスタンプの秒数を取得
ちょっと意味は違うかも…
sanmple
package main import ( "fmt" "time" ) func main(){ _now := time.Now() fmt.Println(_now.Unix()) fmt.Println(_now.UnixNano()) }
↓
$ go run time3.go 1453212000 1453212000553662868 $
日付加算
日数、月数、年数を指定して加算、減算を行う
sample
package main import ( "fmt" "time" ) func main(){ // 現在の日時を取得 _now := time.Now() fmt.Println(_now) fmt.Println("") // 日付計算 fmt.Println(_now.AddDate(0, 0, 10).Format(time.RFC3339)) fmt.Println(_now.AddDate(0, 2, 0).Format(time.RFC3339)) fmt.Println(_now.AddDate(5, 0, 0).Format(time.RFC3339)) fmt.Println("") fmt.Println(_now.AddDate(0, 0, -10).Format(time.RFC3339)) fmt.Println(_now.AddDate(0, -2, 0).Format(time.RFC3339)) fmt.Println(_now.AddDate(-5, 0, 0).Format(time.RFC3339)) }
↓
$ go run time5.go 2016-01-19 23:06:58.719504358 +0900 JST 2016-01-29T23:06:58+09:00 2016-03-19T23:06:58+09:00 2021-01-19T23:06:58+09:00 2016-01-09T23:06:58+09:00 2015-11-19T23:06:58+09:00 2011-01-19T23:06:58+09:00 $
時間加算
時間を指定して加算、減算を行う
sample
package main import ( "fmt" "time" ) func main(){ // 現在の日時を取得 _now := time.Now() fmt.Println(_now) fmt.Println("") // 日付計算 fmt.Println(_now.Add(time.Duration(25) * time.Second).Format(time.RFC3339)) fmt.Println(_now.Add(time.Duration(12) * time.Minute).Format(time.RFC3339)) fmt.Println(_now.Add(time.Duration(05) * time.Hour).Format(time.RFC3339)) fmt.Println("") fmt.Println(_now.Add(time.Duration(-25) * time.Second).Format(time.RFC3339)) fmt.Println(_now.Add(time.Duration(-12) * time.Minute).Format(time.RFC3339)) fmt.Println(_now.Add(time.Duration(-05) * time.Hour).Format(time.RFC3339)) }
↓
$ go run time6.go 2016-01-19 23:13:56.686245405 +0900 JST 2016-01-19T23:14:21+09:00 2016-01-19T23:25:56+09:00 2016-01-20T04:13:56+09:00 2016-01-19T23:13:31+09:00 2016-01-19T23:01:56+09:00 2016-01-19T18:13:56+09:00 $
文字列を任意のレイアウトより日付に変換
sample
package main import ( "fmt" "time" ) func main(){ _date, _err := time.Parse("2006-01-02 15:04:05", "2001-10-23 15:54:20") if _err != nil { panic(_err) } fmt.Println(_date) }
↓
$ go run time7.go 2001-10-23 15:54:20 +0000 UTC $
2つの日時より差を取得する
sample
package main import ( "fmt" "time" ) func main(){ // 2つの日時を設定 _date1, _ := time.Parse("2006-01-02 15:04:05", "2001-10-23 15:54:20") _date2, _ := time.Parse("2006-01-02 15:04:05", "2002-10-23 15:54:20") // 差を取得する _d := _date2.Sub(_date1) // 差を出力する fmt.Println(_d) }
↓
$ go run time8.go 8760h0m0s $
まあ、こんな感じ
所感
使いそうなものをとりあえず、調べてみた。
日付のフォーマットでちょっとつまづきそうだけど基本は抑えた感じ…多分
基本、手続き型言語の仕事が多すぎた分、慣れるのがちょっと大変かも