Go言語でGO(勉強記録4)

テクノロジー

そろそろ基本文法は置いといて、なんとなく遊べそうなものを作ろうと思います。
特にサーバーバックエンドでも使われてるっぽいので、目指す方向はそっちで。

モジュール管理 go modules

pyenvみたいな感じ?の、モジュール管理機能だそうです。

プロジェクトフォルダにて、以下のコマンドを実行。(当方Windows10)

> go mod init pokete.dev/first

pokete.dev/firstの部分はモジュール名です。

実行すると、go.modというファイルができました。
たぶんここに外部パッケージが増えていくんだとおもう。

ちなみにファイルを見ての通り、go 1.16を使っていますが、Modules周りで大きな変更があったようです。
とりあえず初心者なので、以下の理解をしておきます。

  • go.modの更新は基本的にgo mod tidyで行う。従来はgo buildなどでも書き換わっていた。
  • 外部バイナリ(=buildして出来たexeファイル)のインストールにはgo installを使う。従来はgo getを使っていたが、そのうち消えるらしい。
  • バイナリじゃないパッケージの導入はgo getを使う。

Gin

ジン。

Go言語で一番有名なWebフレームワーク、という理解。
日本での案件数も多いらしい。知らんけど。

入れてみる

go get github.com/gin-gonic/gin

go.modに行が増えました。

試しに使ってみる

以降は↓のQiitaに概ね乗っかります(時々脱線しようと思います)

ファイル構成は、こんな感じにしました。

前回まで使っていたhelloworld.goと同exeは、oldへ退避。

代わりにcontent/index.htmlと、main.goを作りました。

content/index.htmlは、こんなかんじ。

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TestHtml</title>
</head>

<body>
    This is My Homepage!!
</body>

</html>

main.goは、こんなかんじ。
※Go言語のシンタックスハイライターを持ち合わせていない…

package main

import "github.com/gin-gonic/gin"

func main() {
	router := gin.Default()
	router.LoadHTMLGlob("content/*.html")

	router.GET("/", func(ctx *gin.Context) {
		ctx.HTML(200, "index.html", gin.H{})
	})

	router.Run()
}

一旦よく分からないまま書いてみて、go run main.goで動かしてみます。

実行すると、こんなかんじ。
(ネットワーク通信プログラムなので、起動時にWindowsが確認してくる)
(Avastさんが怪しんでCaptureするので、起動まで時間はかかる)

これで localhost:8080 にアクセスすると、

やったぜ。

コンソールのほうにも、アクセスログが出てきました。

サーバはCtrl+Cで止めて、プログラムの中身を見てみます。

コードを見てみる

func main() {
	router := gin.Default()               // デフォルトのルータを生成(そのまま)
	router.LoadHTMLGlob("content/*.html") // レンダリングするHTMLパスを指定

	router.GET("/", func(c *gin.Context) {
		// "/"に対してGETリクエストがあったときになだれ込む
		// 引数cはContext
		c.HTML(200, "index.html", gin.H{})
		// c.HTMLの引数1つめはHTTPステータスコード
		// 2つめはベースとして利用するテンプレート
		// 3つめはテンプレートを埋めるモノ
		// gin.Hはmap[string]interface{}のショートカットらしい
	})

	router.Run() // サーバ機能を開始
	// Run()の引数としてアドレスを取れる
	// なおポート番号は "PORT" の環境変数にセットして代用可
}

分かった範囲を、コメントで書いてみました。

LoadHTMLGlobとgin.H{}は次の節でもう少し見てみることに。

router.Run()のカッコ内は、何も書かない&環境変数PORTが空だと
router.Run(":8080")
引数が空で、環境変数PORTに何か入っていると
router.Run(os.Getenv("PORT"))
引数があると、その引数の値がエンドポイントになる模様。

テンプレート

さっきのindex.htmlをこう変えて

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TestHtml</title>
</head>

<body>
    This is My {{.text}}!! <!-- Homepageを{{.text}}にした -->
</body>

</html>

main.goのmain()をこう変えてみました

func main() {
	router := gin.Default()
	router.LoadHTMLGlob("content/*.html")

	router.GET("/", func(c *gin.Context) {
		c.HTML(200, "index.html", gin.H{"text": "hogehoge"}) // {}に追記
	})

	router.Run()
}

すると、HTMLの{{.text}}に、hogehogeが入ります。
なるほどね。

パスパラメータとクエリストリング

APIを作るにあたって必須の要素。

HTMLはシンプルにしました。

<body>
    {{.text}}!!
</body>

main()はこんな感じ

func main() {
	router := gin.Default()
	router.LoadHTMLGlob("content/*.html")

	router.GET("/:param", func(c *gin.Context) {
		param := c.Param("param")
		query := c.DefaultQuery("query", "くえりさんぷる")
		c.String(200, "Param: %s, Query: %s", param, query)
	})

	router.Run()
}

これで、 localhost:8080/test にアクセスすると、testがParamで取れているのが分かります。

localhost:8080/test?query=piyo にアクセスすると、さらにpiyoがQueryで取れているのが分かります。
ちなみにc.DefaultQuery()はデフォルト値を入れられるタイプ、c.Query()はデフォルト値がないタイプのようです。

次回はこれらを使って、ちょっとしたアプリケーションを作ってみようかと。