どっかの鳥の日常

主に開発についてのブログです。たまに他愛ないことも。

Goでサーバサイド導入

はじめに

1年後にGoを使う可能性があるため、勉強がてら久々のブログ更新をしてみようと思いました。
やることは、ほんとにチュートリアル程度のことなので、何もやったことないよーといった人にしか役立たないと思います。(どちらかといえば知見が欲しい)
Goについてはガチ初心者なので、間違った点などがあればご教授ください。
それではお勉強を始めます。

開発環境

Goのインストール

MacなのでHomebrewでインストールします。

$ brew install go

環境変数

環境変数なのですが、Go 1.11 から追加されたGo Modulesにより必要ない可能性もあるのですが念の為設定しておきます。(要らなければ飛ばしてください)

環境変数config.fishに以下を追記します。

# golang
set -x GOPATH $HOME/go
set -x PATH $PATH $GOPATH/bin

bashの場合は.bash_profileに以下を追記で設定できると思います。(not 検証)

# golang
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin

これでGoの環境は揃いました。

プロジェクトの作成

今回は、先程述べたGo Modulesを使用してモジュール管理をしていきたいと思います。
このページを参考にさせていただきました。 blog.mmmcorp.co.jp

適当にディレクトリを作成して以下のコマンドを打ちます。

$ go mod init <プロジェクト名>

実際の私のプロジェクト名を入れた例はこちらになります。

$ go mod init github.com/tokutatsu/go-web-app-practice

そうすると、go.modファイルが作成されます。

これで準備は整いました。

さぁコーディング

main.goを作成してコードを書いていきます。

まずはHello Worldから

以下を実行してhttp://localhost:8080/helloにアクセスするとHello World!を返してくれます。

package main

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

func main() {
    http.HandleFunc("/hello", helloHandler)
    http.ListenAndServe(":8080", nil)
}

func helloHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello World!")
}

実行するには、Goはコンパイル言語なのでコンパイルして実行という形が一般的です。
しかし、Goではgo runをすると自動でコンパイルと実行をしてくれるため今回はそちらを使用します。
ブラウザ やcurlhttp://localhost:8080/helloにアクセスすると結果が確認できます。

レンダリング

次にhtml/templateを使ってレンダリングをしていきます。
text/templateというのもあるみたいですが、どうもhtml/templateのほうが良さげなので今回はそちらを使用します。
importにhtml/templatの追加が必要です。 先程のコードのfunc main()に以下を追加します。

http.HandleFunc("/template", templateHandler)

これは、ハンドラの関数を追加しただけです。
では、追加するハンドラの関数を定義していきます。 ここでは、テンプレートファイル(HTMLファイル)を読み込んでそれを描画しています。 また、tmpl.Execute()の第2引数に値を指定してあげることで、テンプレートに埋め込むことができます。
この埋め込みは、構造体やマップでも可能です。

func templateHandler(w http.ResponseWriter, r *http.Request) {
    tmpl := template.Must(template.ParseFiles("./views/index.html"))
    tmpl.Execute(w, nil)
}

素朴なHTML(index.html)↓

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Hello</title>
</head>
<body>
  <h1>Hello World!</h1>
</body>
</html>

結果はこんな感じです。
f:id:tokutatsu:20200402233442p:plain

埋め込みを利用したときには以下のようになります。 f:id:tokutatsu:20200402233636p:plain 変更点は、func templateHandler()のtmpl.Execute(w, nil)tmpl.Execute(w, "tokutatsu")にしたことと、index.html<h1>Hello World!</h1><h1>Hello {{.}}!</h1>にしただけです。(簡単)

jsonを返す

APIではjsonを返すことが多いのでやってみます。
まず、importにencoding/jsonを追加します。
次に、構造体を次のように定義してあげます。

type Human struct {
    Name string
    Age  int
}

そして、先ほどと同様にfunc main()で以下を追加。

http.HandleFunc("/json", jsonHandler)

そして、ハンドラの関数を次のように定義していきます。
はじめに先ほど定義した構造体を初期化します。
次に、jsonを返すためにはContent-Typeヘッダにapplication/jsonを指定する必要があるためセットします。

func jsonHandler(w http.ResponseWriter, r *http.Request) {
    my := Human{
        Name: "tokutatsu",
        Age:  21,
    }
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    json.NewEncoder(w).Encode(my)
}

curlで確認した結果がこちらです。

{"Name":"tokutatsu","Age":21}

うまくできましたね。

さいごに

Goの環境すらない状態からWebサーバを構築しました(超簡易)が、標準ライブラリでここまでできるので使い勝手はいいなぁと感じました。
ただ、パッケージの構成は自由度が高いためいい構成などがあれば教えて下さい。
これからいくつかのWebアプリを作りながら、設計などを詰めていければなと。
一応今回のもの↓

github.com

参考ページ

ryomak.info maku77.github.io