L'autre jour, j'ai pensé: "Je n'ai pas l'intention de l'utiliser pour les affaires, mais je devrais connaître Go dans une certaine mesure ...", alors j'ai essayé d'implémenter un simple CRUD en utilisant Go, donc je vais résumer la méthode sous forme de mémorandum. Je vais.
En gros, c'est un peu arrangé en combinant les contenus des sites suivants. Veuillez vous référer à ces sites car ils ont été très utiles dans l'étude de Go. Création d'un environnement de développement Go avec Docker Créez une application Web très simple avec Go / Gin Introduction à Go Language-MySQL Connection- Créer un conteneur de base de données pour docker-compose MySQL8.0 Comment attendre que MySQL démarre avec docker-compose up (2 types introduits)
Tout d'abord, nous allons lancer Go with Docker. Directement sous le répertoire de travail
DockerFile
FROM golang:latest
RUN mkdir /app
WORKDIR /app
docker-compose.yml
version: '3'
services:
go:
build:
context: .
dockerfile: DockerFile
command: /bin/sh -c "go run main.go"
stdin_open: true
tty: true
volumes:
- .:/app
main.go
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
Si vous faites docker-compose up
avec ceci, vous verrez Hello, World! Sur la console.
Si cela se produit, le lancement de Go est réussi.
Je vais expliquer brièvement chaque fichier.
・ DockerFile
Créez un conteneur Go (environnement virtuel).
En spécifiant WORKDIR / app
ici, toutes les opérations suivantes seront effectuées sous / app
.
・ Docker-compose.yml
Ecrivez les paramètres lors du lancement du conteneur créé avec DockerFile.
Cela lancera un conteneur dans DockerFile et lancera la commande go run main.go
pour lancer main.go.
・ Main.go J'écrirai le traitement lié à Go ici. Cette fois, tout ce que vous avez à faire est de sortir Hello, World!.
Maintenant que Go a commencé, créons une page Web à l'aide de Go. Cette fois, j'utiliserai un framework appelé Gin. --Ajouter l'installation à DockerFile
DockerFile
FROM golang:latest
RUN mkdir /app
WORKDIR /app
RUN go get github.com/gin-gonic/gin
docker-compose.yml
version: '3'
services:
go:
build:
context: .
dockerfile: DockerFile
command: /bin/sh -c "go run main.go"
stdin_open: true
tty: true
volumes:
- .:/app
ports:
- 8080:8080
main.go
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.LoadHTMLGlob("templates/*.html")
router.GET("/", func(ctx *gin.Context){
ctx.HTML(200, "index.html", gin.H{})
})
router.Run()
}
templates/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Sample App</title>
</head>
<body>
<h1>Hello World!!!</h1>
</body>
</html>
avec ça
docker-compose build
docker-compose up -d
Après être allé et attendu un moment Si vous accédez à [http: // localhost: 8080](http: // localhost: 8080), vous verrez Hello World!.
Je vais vous expliquer ce que j'ai fait cette fois.
Cette fois, j'ai ajouté un framework appelé Gin.
Gin est installé avec la commande go get github.com / gin-gonic / gin
après avoir créé un conteneur avec DockerFile et est appelé dans main.go.
Et le contenu des modèles est lu dans main.go,
router.GET("/", func(ctx *gin.Context){
ctx.HTML(200, "index.html", gin.H{})
})
Sera associé à templates / index.html pour root ("/"). En passant, si vous changez le premier argument ("/") de router.GET en "/ test" etc., ce ne sera pas [http: // localhost: 8080](http: // localhost: 8080) mais [http: // localhost Index.html sera affiché à l'adresse: 8080 / test](http: // localhost: 8080 / test).
Enfin, ajoutez un port à docker-compose.yml pour autoriser l'accès à localhost: 8080.
À ce stade, vous pouvez créer des pages Web avec Go. Cependant, en réalité, lors de la création d'un service Web, la connexion avec la base de données est inévitable. Ensuite, nous allons lancer MySQL en utilisant Docker.
Premier dans docker-compose.yml -Description du conteneur db ・ Description du volume S'il-vous-plait ajoutez.
docker-compose.yml
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: go_database
MYSQL_USER: go_test
MYSQL_PASSWORD: password
TZ: 'Asia/Tokyo'
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
volumes:
- db-data:/var/lib/mysql
- ./db/my.cnf:/etc/mysql/conf.d/my.cnf
ports:
- 3306:3306
volumes:
db-data:
driver: local
Créez également un répertoire db et créez-y un fichier my.cnf.
my.cnf
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_bin
default-time-zone = SYSTEM
log_timestamps = SYSTEM
default-authentication-plugin = mysql_native_password
[mysql]
default-character-set = utf8mb4
[client]
default-character-set = utf8mb4
(Cette zone est la même que la page de référence. Seule la partie liée au journal n'a pas fonctionné pour une raison quelconque, je l'ai donc supprimée)
Si vous faites docker-compose up -d
jusqu'à présent, le conteneur MySQL devrait également démarrer.
Puisqu'il n'y a qu'une description des paramètres, l'explication ici est omise.
Maintenant que MySQL a démarré, je vais le connecter à Go immédiatement. Cette fois, nous utiliserons le pilote sql et un framework appelé GORM pour la connexion. --Ajouter l'installation à DockerFile
DockerFile
FROM golang:latest
RUN mkdir /app
WORKDIR /app
RUN go get github.com/gin-gonic/gin
RUN go get github.com/go-sql-driver/mysql
RUN go get github.com/jinzhu/gorm
docker-compose.yml
version: '3'
services:
go:
build:
context: .
dockerfile: DockerFile
command: /bin/sh -c "go run main.go"
stdin_open: true
tty: true
volumes:
- .:/app
ports:
- 8080:8080
depends_on:
- "db"
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: go_database
MYSQL_USER: go_test
MYSQL_PASSWORD: password
TZ: 'Asia/Tokyo'
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
volumes:
- db-data:/var/lib/mysql
- ./db/my.cnf:/etc/mysql/conf.d/my.cnf
ports:
- 3306:3306
volumes:
db-data:
driver: local
main.go
package main
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/go-sql-driver/mysql"
)
func main() {
db := sqlConnect()
defer db.Close()
router := gin.Default()
router.LoadHTMLGlob("templates/*.html")
router.GET("/", func(ctx *gin.Context){
ctx.HTML(200, "index.html", gin.H{})
})
router.Run()
}
func sqlConnect() (database *gorm.DB) {
DBMS := "mysql"
USER := "go_test"
PASS := "password"
PROTOCOL := "tcp(db:3306)"
DBNAME := "go_database"
CONNECT := USER + ":" + PASS + "@" + PROTOCOL + "/" + DBNAME + "?charset=utf8&parseTime=true&loc=Asia%2FTokyo"
count := 0
db, err := gorm.Open(DBMS, CONNECT)
if err != nil {
for {
if err == nil {
fmt.Println("")
break
}
fmt.Print(".")
time.Sleep(time.Second)
count++
if count > 180 {
fmt.Println("")
fmt.Println("Échec de la connexion à la base de données")
panic(err)
}
db, err = gorm.Open(DBMS, CONNECT)
}
}
fmt.Println("Connexion à la base de données réussie")
return db
}
Maintenant, faites docker compose up
et si la console dit" Connexion DB réussie ", c'est réussi.
Puisque sqlConnect est le contenu principal ajouté, je vais l'expliquer.
func sqlConnect() (database *gorm.DB) {
DBMS := "mysql"
USER := "go_test"
PASS := "password"
PROTOCOL := "tcp(db:3306)"
DBNAME := "go_database"
CONNECT := USER + ":" + PASS + "@" + PROTOCOL + "/" + DBNAME + "?charset=utf8&parseTime=true&loc=Asia%2FTokyo"
count := 0
db, err := gorm.Open(DBMS, CONNECT)
if err != nil {
for {
if err == nil {
fmt.Println("")
break
}
fmt.Print(".")
time.Sleep(time.Second)
count++
if count > 180 {
fmt.Println("")
fmt.Println("Échec de la connexion à la base de données")
panic(err)
}
db, err = gorm.Open(DBMS, CONNECT)
}
}
fmt.Println("Connexion à la base de données réussie")
return db
}
La première moitié définit les informations de connexion à la base de données. Veuillez entrer le contenu défini dans docker-compose.yml.
Puis connectez-vous à la base de données avec db, err: = gorm.Open (SGBD, CONNECT)
. Cependant, en fonction de l'heure de démarrage de MySQL, MySQL peut ne pas être prêt au moment où cette commande est exécutée.
Par conséquent, ce code prend deux mesures.
Le premier est le paramètre de dépendance dans docker-compose.yml. En définissant depend_on ici, le conteneur go sera démarré après le démarrage du conteneur db.
Le second est une nouvelle tentative de traitement. Même après le démarrage du conteneur db, MySQL met du temps à démarrer, donc s'il ne se connecte pas à la base de données, j'attends 1 seconde puis réessaye. Si tel est le cas, il continuera à réessayer en cas d'erreur, renvoyez donc l'erreur un nombre approprié de fois. Dans ce code, s'il ne se connecte pas pendant 3 minutes, une erreur se produira.
Enfin, il est connecté à MySQL, alors implémentons enfin le traitement CRUD et voyons le flux réel. Seuls main.go et index.html seront modifiés. --Créer la définition de l'utilisateur --Migration --Mise en place du post-traitement
main.go
package main
import (
"fmt"
"strconv"
"time"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/go-sql-driver/mysql"
)
type User struct {
gorm.Model
Name string
Email string
}
func main() {
db := sqlConnect()
db.AutoMigrate(&User{})
defer db.Close()
router := gin.Default()
router.LoadHTMLGlob("templates/*.html")
router.GET("/", func(ctx *gin.Context){
db := sqlConnect()
var users []User
db.Order("created_at asc").Find(&users)
defer db.Close()
ctx.HTML(200, "index.html", gin.H{
"users": users,
})
})
router.POST("/new", func(ctx *gin.Context) {
db := sqlConnect()
name := ctx.PostForm("name")
email := ctx.PostForm("email")
fmt.Println("create user " + name + " with email " + email)
db.Create(&User{Name: name, Email: email})
defer db.Close()
ctx.Redirect(302, "/")
})
router.POST("/delete/:id", func(ctx *gin.Context) {
db := sqlConnect()
n := ctx.Param("id")
id, err := strconv.Atoi(n)
if err != nil {
panic("id is not a number")
}
var user User
db.First(&user, id)
db.Delete(&user)
defer db.Close()
ctx.Redirect(302, "/")
})
router.Run()
}
func sqlConnect() (database *gorm.DB) {
DBMS := "mysql"
USER := "go_test"
PASS := "password"
PROTOCOL := "tcp(db:3306)"
DBNAME := "go_database"
CONNECT := USER + ":" + PASS + "@" + PROTOCOL + "/" + DBNAME + "?charset=utf8&parseTime=true&loc=Asia%2FTokyo"
count := 0
db, err := gorm.Open(DBMS, CONNECT)
if err != nil {
for {
if err == nil {
fmt.Println("")
break
}
fmt.Print(".")
time.Sleep(time.Second)
count++
if count > 180 {
fmt.Println("")
panic(err)
}
db, err = gorm.Open(DBMS, CONNECT)
}
}
return db
}
templates/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Sample App</title>
</head>
<body>
<h2>Ajouter un utilisateur</h2>
<form method="post" action="/new">
<p>Nom<input type="text" name="name" size="30" placeholder="Entrez s'il vous plait" ></p>
<p>adresse mail<input type="text" name="email" size="30" placeholder="Entrez s'il vous plait" ></p>
<p><input type="submit" value="Send"></p>
</form>
<h2>Liste d'utilisateur</h2>
<table>
<tr>
<td>Nom</td>
<td>adresse mail</td>
</tr>
{{ range .users }}
<tr>
<td>{{ .Name }}</td>
<td>{{ .Email }}</td>
<td>
<form method="post" action="/delete/{{.ID}}">
<button type="submit">Supprimer</button>
</form>
</td>
</tr>
{{ end }}
</ul>
</body>
</html>
Maintenant, faites docker-compose up -d
, et lorsque vous accédez à [http: // localhost: 8080](http: // localhost: 8080), le formulaire d'inscription de l'utilisateur apparaîtra, et lorsque vous enregistrez l'utilisateur, l'utilisateur enregistré ci-dessous Les informations seront affichées.
De plus, même si vous supprimez le conteneur et le relancez, l'utilisateur enregistré ne sera pas supprimé et restera affiché dans la liste des utilisateurs.
Ensuite, je vais expliquer la partie supplémentaire.
Tout d'abord, je crée une structure appelée User dans main.go. Dans gorm.Model
, le contenu nécessaire pour le modèle, tel que l'identifiant, est placé dans Utilisateur, et le nom et l'adresse e-mail spécifiques à l'utilisateur sont ajoutés.
Cette structure est reflétée dans la base de données par db.AutoMigrate
.
Ensuite, nous implémenterons le traitement CRUD dans chaque chemin.
Obtenez la liste des utilisateurs dans le chemin racine. Utilisez db.Find (& users) pour obtenir la liste des utilisateurs dans la base de données en tant que structure User. En plaçant une commande entre les deux, l'ancien utilisateur apparaît au moment de l'acquisition. Nous transmettons le dernier utilisateur récupéré à index.html.
Le chemin / new crée un utilisateur basé sur le contenu du formulaire. J'obtiens le contenu soumis par le formulaire avec ctx.PostForm et je persiste le contenu avec db.Create. Redirigez vers root lorsque vous avez terminé.
Le chemin / delete spécifie l'id pour supprimer l'utilisateur. Ici, l'identifiant de l'utilisateur est spécifié dans l'URL, mais il est également obtenu à partir de ctx. Ensuite, récupérez l'utilisateur à partir du contenu avec db.First et supprimez l'utilisateur avec db.Delete. Notez que l'id est passé sous forme de chaîne, donc strconv.Atoi le convertit en type int.
Dans index.html, le formulaire et la table sont créés par la méthode d'écriture html générale.
Ici, nous recevons les utilisateurs passés de main.go sous la forme {{range .users}}
.
Cette fois, j'ai essayé d'implémenter un CRUD simple avec Go + MySQL + Docker comme introduction au développement de services Web avec Go. C'est juste une pratique, donc je ne pense pas à la validation ou au contrôle fin. Le contenu que j'ai créé cette fois est rudimentaire, mais je pense que vous pouvez réellement créer un service Web en développant et en compliquant ce contenu. Si quelqu'un veut faire quelque chose avec Go, veuillez vous y référer!
Recommended Posts