【自己学習用】はじめてのGo4


encoding/jsonパッケージ

JSONを扱うためにはencoding/jsonパッケージを用いる。
構造体にデータを代入し、ポインタをjson.Marshal()に渡すだけで,デフォルトのフォーマットでJSON文字列の[]byteを生成できる。

type Person struct {
    ID      int
    Name    string
    Email   string
    Age     int
    Address string
    memo    string
}

func main() {
    person := &Person{
        ID:      1,
        Name:    "Gopher",
        Email:   "[email protected]",
        Age:     5,
        Address: "",
        memo:    "golang lover",
    }
    b, err := json.Marshal(person)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(string(b)) // 文字列に変換
}

/* 出力結果
{
  "ID": 1,
  "Name": "Gopher",
  "Email": "[email protected]",
  "Age": 5,
  "Address": ""
}
*/

小文字で始まるプライベートなメンバは出力結果に含まれない。
パブリックなメンバを出力しないようにしたり、結果のメンバ名を変えたい場合は構造体にタグを記述する。
プライベートなメンバを出力するようにするにはメンバ名を変更するしかない模様。

/*
`json:"name"` // nameというキーで格納する
`json:"-"` // JSONに格納しない
`json:",omitempty"` // 値が空なら無視
`json:",string"` // 値をJSONとして格納
*/

type Person struct {
    ID int `json:"id"`
    Name string `json:"name"`
    Email string `json:"-"`
    Age int `json:"age"`
    Address string `json:"address,omitempty"`
    memo string
}

JSONから構造体への変換

逆にJSONの文字列からデータをマップした構造体を生成するには,json.Unmarshal()を使用する。

func main() {
    var person Person
    b := []byte(`{"id":1,"name":"Gopher","age":5}`)
    err := json.Unmarshal(b, &person)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(person) // {1 Gopher 5 }
}

ファイルの書き込み

他の言語と大して変わらないので省略!

net/httpパッケージ

簡単なhttpサーバの実装。

package main

import (
    "fmt"
    "net/http"

)

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

    fmt.Fprint(w, "hello world")
}

func main() {
    http.HandleFunc("/", IndexHandler)
    http.ListenAndServe(":3000", nil)
}

http.HandleFunc()でルーティングの設定。
ルートパス(/)に対するリクエスト(IndexHandler)を設定。
http.ListenAndServe()でポート指定してサーバを起動。
http://localhost:3000/
をブラウザから実行すると"hello world"が表示される。

POST

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
)

type Person struct {
    ID int `json:"id"`
    Name string `json:"name"`
}

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

    fmt.Fprint(w, "hello world")
}

func PersonHandler(w http.ResponseWriter,
    r *http.Request) {
    defer r.Body.Close() // 処理の最後にBodyを閉じる

    if r.Method == "POST" {
        // リクエストボディをJSONに変換
        var person Person
        decoder := json.NewDecoder(r.Body)
        err := decoder.Decode(&person)
        if err != nil { // エラー処理
            log.Fatal(err)
        }

        // ファイル名を {id}.txtとする
        filename := fmt.Sprintf("%d.txt", person.ID)
        file, err := os.Create(filename) // ファイルを生成
        if err != nil {
            log.Fatal(err)
        }
        defer file.Close()

        // ファイルにNameを書き込む
        _, err = file.WriteString(person.Name)
        if err != nil {
            log.Fatal(err)
        }

        // レスポンスとしてステータスコード201を送信
        w.WriteHeader(http.StatusCreated)
    }
}

func main() {
    http.HandleFunc("/", IndexHandler)
    http.HandleFunc("/persons", PersonHandler) //curl http://localhost:3000/persons -d '{"id":1,"name":"gopher"}'
    http.ListenAndServe(":3000", nil)
}

html/templateパッケージ

テンプレートの作成

テンプレートを作成し、その中にパラメータを埋め込んで使用する。
構造体の値を埋め込むなら.〇〇。

<!DOCTYPE html>
<title>person</title>
<h1>{{ .ID }} : {{ .Name }}</h1>

使用するときはコンパイルしてから。
テンプレートのコンパイルはParseFiles()を用いる。

テンプレートへの値の埋め込み

コンパイルしたテンプレートに実際に値を埋め込むには,Execute()を用いる。
第二引数に渡した構造体がテンプレートの{{ .〇〇 }}にセットされる。

package main

import (
    "encoding/json"
    "fmt"
    "html/template"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    "strconv"
)

type Person struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

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

    fmt.Fprint(w, "hello world")
}

// テンプレートのコンパイル
var t = template.Must(template.ParseFiles("index.html"))

func PersonHandler(w http.ResponseWriter,
    r *http.Request) {
    defer r.Body.Close() // 処理の最後にBodyを閉じる

    if r.Method == "POST" {
        // リクエストボディをJSONに変換
        var person Person
        decoder := json.NewDecoder(r.Body)
        err := decoder.Decode(&person)
        if err != nil { // エラー処理
            log.Fatal(err)
        }

        // ファイル名を{id}.txtとする
        filename := fmt.Sprintf("%d.txt", person.ID)
        file, err := os.Create(filename) // ファイルを生成
        if err != nil {
            log.Fatal(err)
        }
        defer file.Close()

        // ファイルにNameを書き込む
        _, err = file.WriteString(person.Name)
        if err != nil {
            log.Fatal(err)
        }

        // レスポンスとしてステータスコード201を送信
        w.WriteHeader(http.StatusCreated)
    } else if r.Method == "GET" {
        // パラメータを取得
        id, err := strconv.Atoi(r.URL.Query().Get("id"))
        if err != nil {
            log.Fatal(err)
        }

        filename := fmt.Sprintf("%d.txt", id)
        b, err := ioutil.ReadFile(filename)
        if err != nil {
            log.Fatal(err)
        }

        // personを生成
        person := Person{
            ID:   id,
            Name: string(b),
        }

        // レスポンスにエンコーディングしたHTMLを書き込む
        t.Execute(w, person)
    }
}

func main() {
    http.HandleFunc("/", IndexHandler)
    http.HandleFunc("/persons", PersonHandler)
    //POST curl http://localhost:3000/persons -d '{"id":1,"name":"gopher"}'
    //GET  http://localhost:3000/persons?id=1
    http.ListenAndServe(":3000", nil)
}

ここまででGoの言語学習は終了。
Goの印象としては
分かりにくいところはなくjava等が理解できれば問題なく理解可能
例外処理がなくてpanicになっている箇所くらい?
ラムダ式は使えない・・・?調べてもちょっとわからなかった
html/templateパッケージは実際どの程度使用されているのか疑問
vueやreactとの相性は?という点で
次回からGoのWebFrameWorkを選定していく。
その中で疑問点は明らかにしていきたい。