はじめに
どもです。
最近、Webアプリケーションを久しぶりに作るってなって、GoでのCRUDの書き方等を忘れたので、メモといった感じで記していきます。
ルーティングとCRUDについて詳しく書いていきます。
githubに今回作成した**ソースコード**を載せておきます
使用する環境
実際の環境
項目 | 使用したもの |
---|---|
OS | CentOS8 |
DB | MySQL |
バックエンド | Go |
Goで使用したパッケージ
用途 | パッケージ名 |
---|---|
ルーティング | gin |
ORM | gorm |
開発環境に関しては、すでに終わっていることを仮定します。
実装する内容
今回作成するものは、元から存在するアプリケーションのユーザの管理をするアプリケーションです。
本来、gormのマイグレーションというのもできますが、今回はすでにテーブル等が作成されているものとします。
- ユーザの一覧(Select)
- ユーザの詳細(Select)
- ユーザの削除(Delete)
- ユーザの編集(Update)
といったものを実装していきます。
ディレクトリ構造
.
|- handler.go //DBにCRUDする関数を書く
|- main.go //GETとPOSTの処理を書く
|- routing.go //ルーティングの関数を書く
|- views/
|- css/
| |- main.css
|- html/
| |- delete.html
| |- detail.html
| |- index.html
| |- update.html
|- js/
|- main.js
gin
ginのGoDocは**こちら**から
ginというのは、GoのWebフレームワークで、とりあえず早い。ライバルとしてはechoがあります。
自分的には、どっちでもいいのかなと思います。ただ、ginのほうが簡潔に書けるという印象があります
実装
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.LoadHTMLGlob("./views/html/*.html")
r.Static("/css", "./views/css")
r.GET("/", index)
r.GET("/detail/:id", detail)
r.GET("/delete/:id", delete)
r.GET("/update/:id", update)
r.POST("/do_delete/:id", do_delete)
r.POST("/do_update/:id", do_update)
r.Run(":80")
}
package main
import (
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
func index(c *gin.Context) {
users, err := UserGetAll()
if err != nil {
fmt.Println(err)
c.AbortWithStatus(http.StatusInternalServerError)
return
}
c.HTML(http.StatusOK, "index.html", gin.H{"users": users})
}
~~略~~
func do_update(c *gin.Context) {
n := c.Param("id")
name := c.PostForm("name")
email := c.PostForm("email")
id, err := strconv.Atoi(n)
if err != nil {
fmt.Println(err)
c.AbortWithStatus(http.StatusInternalServerError)
return
}
err = UserUpdate(id, name, email)
if err != nil {
fmt.Println(err)
c.AbortWithStatus(http.StatusInternalServerError)
return
}
c.Redirect(302, "/")
}
解説
主に使っている関数をピックアップして、紹介します。
main.go
まずはここ。main関数の
r := gin.Default()
こいつで、r
ってのがginパッケージ使いまっせーの宣言をする。r
変数は好きにつけてぐださい
r.LoadHTMLGlob("./views/html/*.html")
r.Static("/css", "./views/css")
r.GET("/", index)
r.POST("/do_delete/:id", do_delete)
r.Run(":80")
r
を用いて、このような関数を呼び出してます。
LoadHTMLGlob
:htmlファイルの場所の指定
Static
:css等の静的ファイルの指定(jsでも同様)
GET
:GETの処理(第一引数がドメイン以降のパス指定、第二引数がそのパスに対応した関数の呼び出し)
POST
:POSTの処理(第一引数がドメイン以降のパス指定、第二引数がそのパスに対応した関数の呼び出し)
Run
:サーバの起動(:80は80番ポートで起動してます)
GETとPOSTで呼び出す関数は、次のrouting.go
に書いてます。
routing.go
GET
とPOST
という処理があって、とりあえずGET
だったらHTMLを出力してPOST
だったらリダイレクトする的なイメージがあると思います。
とりあえず、各関数の引数にc *gin.Context
とありますが、先ほどのr
みたいな感じです。
関数の解説
c.AbortWithStatus(http.StatusInternalServerError)
c.HTML(http.StatusOK, "index.html", gin.H{"users": users})
c.Redirect(302, "/")
c
を用いて、このような関数を呼び出してます。
AbortWithStatus(http.StatusInternalServerError)
:リクエストの認証に失敗したら、401を返す
HTML
:htmlファイルを返す。(第一引数は置いておいて、第二引数はhtmlファイルの指定、第三引数はhtmlに値を渡す変数の定義)
Redirect
:リダイレクトをする。(第一引数で302で返す宣言、第二引数でリダイレクト先の指定)
HTMLに変数を渡す場合の処理は、Go Template
とかで検索すると、いろいろ出てくるので、そちらをどうぞ。
main.go
とrouting.go
は以上。これで、ページ遷移くらいはできると思います。単にHTMLを出力するサンプルコードを載せて、次に行きます。
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.LoadHTMLGlob("./views/html/*.html")
r.GET("/", index)
r.Run(":80")
}
func index(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{})
}
これで、index.html
は、出力するはず。(エラーだったらコメントください)
gorm
gormの公式ドキュメントは**こちら**から
gormというのは、Go用のORMライブラリで、DB操作を簡単にできるすげーいいやつ。
公式ドキュメントが超優秀なので、ぜひ見てみてください
実装
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type Users struct {
Id int
Name string
Email string
Password string
}
type Posts struct {
Id int
Author int
Title string
Content string
Comments int
}
const connect = "root:root@tcp(127.0.0.1:3306)/laravel?charset=utf8mb4&parseTime=True&loc=Local"
func UserGetAll() ([]Users, error) {
db, err := gorm.Open(mysql.Open(connect), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
})
if err != nil {
return nil, fmt.Errorf("UserGetAll失敗: %w", err)
}
var users []Users
err = db.Select("id", "name", "email", "password").Find(&users).Error
if err != nil {
return nil, fmt.Errorf("UserGetAll_Select失敗: %w", err)
}
return users, nil
}
func UserGetOne(id int) (Users, error) {
db, err := gorm.Open(mysql.Open(connect), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
})
var user Users
if err != nil {
return Users{}, fmt.Errorf("UserGetOne失敗: %w", err)
}
err = db.First(&user, id).Error
if err != nil {
return Users{}, fmt.Errorf("UserGetOne_First失敗: %w", err)
}
return user, nil
}
func PostGetAll(author int) ([]Posts, error) {
db, err := gorm.Open(mysql.Open(connect), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
})
if err != nil {
return nil, fmt.Errorf("PostGetAll失敗: %w", err)
}
var posts []Posts
err = db.Where("author = ?", author).Find(&posts).Error
if err != nil {
return nil, fmt.Errorf("PostGetAll_Select失敗: %w", err)
}
return posts, nil
}
func UserDelete(id int) error {
db, err := gorm.Open(mysql.Open(connect), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
})
if err != nil {
return fmt.Errorf("UserDelete失敗: %w", err)
}
var users Users
err = db.Delete(&users, id).Error
if err != nil {
return fmt.Errorf("UserDelete_Delete失敗: %w", err)
}
return nil
}
func UserUpdate(id int, name string, email string) error {
db, err := gorm.Open(mysql.Open(connect), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
})
if err != nil {
return fmt.Errorf("UserUpdate失敗: %w", err)
}
var users Users
err = db.First(&users, id).Error
if err != nil {
return fmt.Errorf("UserUpdate_First失敗: %w", err)
}
users.Name = name
users.Email = email
db.Save(&users)
return nil
}
解説
handler.go
各関数の最初に書かれている、gorm.Open
が最重要で、DB(MySQL)に接続する関数で、count
で定義したDB情報を呼び出して使ってます。
自分の関数の命名についてですが、〇〇GetAll
だと要素の全取得、〇〇GetOne
だと一つだけ取得、〇〇Delete
だと削除、〇〇Update
だと更新、みたいな感じです
流れとしては、Users
やPosts
のように、必要な情報を構造体として扱って処理をします。
ここでは、gormの関数が多いので、よく使うものだけピックアップします。
関数の解説
First
:なにか指定したものの最初にマッチした要素を取得する
Where
:データの特定をするために、カラムを指定する
Find
:Firstと異なり、特定の一つのデータを取得する
と、公式ドキュメントにもありますが、関数とSQL文がいい具合に処理できるので、すごく便利。
自分の場合、err
としてエラーハンドリングをしつつ処理をしています。
(確かGoのすごい人からおしえてもらった書き方だった気がする…)
終わり
かなり長くなってしまいましたが、これで解説は終わりです。
Goを用いたWebアプリケーション開発というのは、これからもっとはやっていくと思います。そこで、この記事が参考になることを祈ります。
疲れた…