"BOKU"のITな日常

BOKUが勉強したり、考えたことを頭の整理を兼ねてまとめてます。

GO言語(golang)/Webアプリケーション(2)「gin」&ソース分割&画面遷移&パラメータ受け渡し

f:id:arakan_no_boku:20210412005751p:plain

目次

今回は、画面遷移をやります。

Webフレームワーク「gin」を使います。

ソース分割用のフォルダ構成

前回は「main.go」の「main」ファンクション内にすべての処理を書きました。

arakan-pgm-ai.hatenablog.com

そのまま複数画面にして、かつ、処理を追加すると、とてもごちゃごちゃした可読性の低いソースになってしまいますので、先にソース分割します。

フォルダ構成はこんな感じにします。

f:id:arakan_no_boku:20210606165652p:plain

templatesフォルダにHTMLを置くのは、前回と同じです。

controlsフォルダには、HTMLと1:1の関係になるGo言語のソースを置きます。

例えば、「index.html」に対応するGoのソースは「index.go」みたいにします。

routerフォルダには、routerの生成や各HTMLのロードなどの共通処理のGo言語のソースを置き、main.goには最低限の記述だけ残すようにしていきます。

前回のmain.goを分割する

今回HTML部分は変更しません。

前回のmain.goを分割していきます。

まず、前回のmain.goを再掲します。

package main

import (
	"net/http"

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

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

	title := "値受け渡しのサンプル"

	type Data struct {
		Left  string
		Right string
	}

	type datas []*Data

	var darr datas
	d1 := new(Data)
	d1.Left = "1行目の左側"
	d1.Right = "1行目の右側"
	darr = append(darr, d1)
	d2 := new(Data)
	d2.Left = "2行目の左側"
	d2.Right = "2行目の右側"
	darr = append(darr, d2)

	router.GET("/", func(ctx *gin.Context) {
		ctx.HTML(http.StatusOK, "index.html", gin.H{"title": title, "data": darr})
	})

	router.Run()
}

この中のindex.html用のデータを変数にセットし、HTMLに渡す部分を、まず「controls/index.go」に切り出します。 

package controls

import (
	"net/http"

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

func IndexAction(ctx *gin.Context) {
	title := "値受け渡しのサンプル"

	type Data struct {
		Left  string
		Right string
	}

	type datas []*Data

	var darr datas
	d1 := new(Data)
	d1.Left = "1行目の左側"
	d1.Right = "1行目の右側"
	darr = append(darr, d1)
	d2 := new(Data)
	d2.Left = "2行目の左側"
	d2.Right = "2行目の右側"
	darr = append(darr, d2)

	ctx.HTML(http.StatusOK, "index.html", gin.H{"title": title, "data": darr})

}

functionの名前はわかりやすさ優先で「IndexAction」としました。 

今度は、routerの設定を行う部分を「router/router.go]に切り出します。

package router

import (
	"webPrac/controls"

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

func GetRouter() *gin.Engine {
	r := gin.Default()
	r.LoadHTMLGlob("templates/*.html")

	r.GET("/", controls.IndexAction)

	return r
}

前回初期作成「go mod init」でモジュール名を「webPrac」にしているので、インポートは「webPrac//controls」でその下の同じパッケージ内のfanctionを参照できます。

なお、ソースが別々でもパッケージが同じなら、ひとくくりに扱えます。

このように、処理を別ソースに切り出したことで、main.goのmainファンクションはこんなにシンプルになります。

package main

import (
	"webPrac/router"
)

func main() {
	r := router.GetRouter()
	r.Run()
}

これで実行すると、当然ながら、前回と同じ画面を表示します。 

f:id:arakan_no_boku:20210604011921p:plain

とりあえず、ここまではOKです。

画面を追加して<a>タグで画面遷移&?パラメータの受け渡し

画面をもうひとつ追加します。

ソース分割したので、画面追加は以下の手順になります。

  1. templatesの下にHTMLを追加する。
  2. 対応するGoプログラムを、controllsの下に追加する。
  3. ruter/ruter.goに上記のHTMLとGoのファンクションを追加する。

まずは、templatesの下に「inputSample.html」を作成します。

CSSは、new.cssを使います。

newcss.net

<!DOCTYPE html>
<html lang="ja">
	<head>
		<meta charset="UTF-8" />
		<meta name="description" content="HTML5 exsample" />
		<meta name="author" content="boku" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<link rel="stylesheet" href="https://fonts.xz.style/serve/inter.css" />
		<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css" />
		<title>Hello</title>
	</head>
	<body>
		<h1>{{.title}}</h1>
		<form method="POST" action="/">
			<table>
				<tr>
					<th>適当な文字列を入力</th>
					<td><input type="text" name="sampletext" style="border: solid 1px" /></td>
				</tr>
				<tr>
					<td colspan="2"><input type="submit" value="画面遷移" /></td>
				</tr>
			</table>
		</form>
	</body>
</html>

テキストボックスが一つあるだけの簡単な入力画面です。 

この画面を表示するGo言語のソースはHTMLとあわせて「inputSample.go」とします。

package controls

import (
	"net/http"

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

func InputSampleAction(ctx *gin.Context) {
	title := ctx.Query("title")

	ctx.HTML(http.StatusOK, "inputSample.html", gin.H{"title": title})

}

そして、この画面を対応する定義を「router.go」に追加します。 

r.GET("/inputSample", controls.InputSampleAction)

こうしておくことで、別のHTMLから以下のようにしてリンクで遷移できます。

 <a href="/inputSample?title=何かを入力するサンプル">遷移します。</a>

このリンクには「?title=何かを入力するサンプル"」のように?でパラメータをつけています。

これを、Go言語の「InputSampleAction」内で受け取っているのが

title := ctx.Query("title")

の部分です。

リンクを使った画面遷移とパラメータの受け渡しは、このパターンでできます。

Submitボタン&POSTで画面遷移と入力データの受け渡し

上記の「inputSample.html」の中に一つのテキストボックスと、submitボタンを置いてます。

<form method="POST" action="/">

としているので、ボタンを押すと「/」・・つまり、index.htmlにPOSTで遷移します。

これを受けるためには、まず「index.go」にPOST用のアクションを追加します。

func IndexPostAction(ctx *gin.Context) {
	title := "POSTで値を受け取るサンプル"
	txt := ctx.PostForm("sampletext")
	darr := strings.Split(txt, ",")
	ctx.HTML(http.StatusOK, "index.html", gin.H{"title": title,"data":darr})
}

入力内容を受け取る部分は 

 txt := ctx.PostForm("sampletext")

 です。

sampletextという名前は、HTML中でテキストボックスを

<input type="text" name="sampletext" style="border: solid 1px" />

のようにnameを定義しているからです。

そして、POSTで「/」への遷移をうけられるように、router.goに、以下を追加します。

r.POST("/", controls.IndexPostAction)

Action内では、テキストボックスの入力値を受け取って、カンマ(,)をデリミタにして分割したstringの配列を、HTMLに渡してます。

それを受けてインデックスと同時に表示できるように「index.html」を修正しました。

{{range $index,$value := .data}}
	<tr>
		<th>インデックス{{$index}}</th>
		<td>{{$value}}</td>
	</tr>
{{end}}

これで一通りはできました。

実行イメージです

main.goを実行して「http://localhost:8080/」でブラウザからアクセスすると。

f:id:arakan_no_boku:20210608003405p:plain

ここから、「入力サンプルの画面に遷移します」リンクをクリックすると。

f:id:arakan_no_boku:20210608003509p:plain

このテキストボックスに「ひとつめ,ふたつめ,みっつめ」と入力して「画面遷移」ボタンを押します。

f:id:arakan_no_boku:20210608003641p:plain

よしよし、ちゃんと動いてます。

最後に上記にのせていないソース全文です

index.html の修正版です。

<!DOCTYPE html>
<html lang="ja">
	<head>
		<meta charset="UTF-8" />
		<meta name="description" content="HTML5 exsample" />
		<meta name="author" content="boku" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<link rel="stylesheet" href="https://fonts.xz.style/serve/inter.css" />
		<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css" />
		<title>Hello</title>
	</head>
	<body>
		<h1>{{.title}}</h1>
		<table>
			{{range $index,$value := .data}}
			<tr>
				<th>インデックス{{$index}}</th>
				<td>{{$value}}</td>
			</tr>
			{{end}}
			<tr>
				<td colspan="2"><a href="/inputSample?title=何かを入力するサンプル">入力サンプルの画面に遷移します。</a></td>
			</tr>
		</table>
	</body>
</html>

次に「index.go」の修正版です 

package controls

import (
	"net/http"
	"strings"

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

func IndexAction(ctx *gin.Context) {
	title := "GETで遷移してきたサンプル"

	type datas [] string

	var darr datas
	darr = append(darr, "GETで遷移してきたときの1行目")
	darr = append(darr, "GETで遷移してきたときの2行目のテキスト")

	ctx.HTML(http.StatusOK, "index.html", gin.H{"title": title, "data": darr})

}

func IndexPostAction(ctx *gin.Context) {
	title := "POSTで値を受け取るサンプル"
	txt := ctx.PostForm("sampletext")
	darr := strings.Split(txt, ",")
	ctx.HTML(http.StatusOK, "index.html", gin.H{"title": title,"data":darr})
}

最後に「router.go」です。

package router

import (
	"webPrac/controls"

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

func GetRouter() *gin.Engine {
	r := gin.Default()
	r.LoadHTMLGlob("templates/*.html")

	r.GET("/", controls.IndexAction)
	r.POST("/", controls.IndexPostAction)
	r.GET("/inputSample", controls.InputSampleAction)

	return r
}

今回はこんなところで。

ではでは。