概要
Go言語でウェブサーバーを構築のためのフレームワークはEchoなど幾つか有りますがAPIとミドルウェアのサポートもありパフォーマンスもよいといわれてるGinについて記述したいと思います。
editorはvscodeをお勧めします。コードを作成して保存するとフォーマッティングもやってくれますしインポートも助けてます。
フォルダー
controller, serviceフォルダーを作成して構築してみたいと思います。
ウェブサーバー構築
プロジェクトを生成
$ mkdir go-gin-test
$ go mod init example.com/example-gin
go: creating new go.mod: module example.com/example-gin
フレームワークインポート
$ go get github.com/gin-gonic/gin
go: added github.com/bytedance/sonic v1.9.1
go: added github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311
go: added github.com/gabriel-vasile/mimetype v1.4.2
go: added github.com/gin-contrib/sse v0.1.0
go: added github.com/gin-gonic/gin v1.9.1
go: added github.com/go-playground/locales v0.14.1
go: added github.com/go-playground/universal-translator v0.18.1
go: added github.com/go-playground/validator/v10 v10.14.0
go: added github.com/goccy/go-json v0.10.2
go: added github.com/json-iterator/go v1.1.12
go: added github.com/klauspost/cpuid/v2 v2.2.4
go: added github.com/leodido/go-urn v1.2.4
go: added github.com/mattn/go-isatty v0.0.19
go: added github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
go: added github.com/modern-go/reflect2 v1.0.2
go: added github.com/pelletier/go-toml/v2 v2.0.8
go: added github.com/twitchyliquid64/golang-asm v0.15.1
go: added github.com/ugorji/go/codec v1.2.11
go: added golang.org/x/arch v0.3.0
go: added golang.org/x/crypto v0.9.0
go: added golang.org/x/net v0.10.0
go: added golang.org/x/sys v0.8.0
go: added golang.org/x/text v0.9.0
go: added google.golang.org/protobuf v1.30.0
go: added gopkg.in/yaml.v3 v3.0.1
$
コード作成
service package
serviceフォルダーにserviceパッケージでuser.service.goを作成します。
$ mkdir service
$ vi service/user.service.go
package service
type User struct {
ID int32 `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
}
var users = []User{
{ID: 0, Email: "user1@example.com", Name: "user1"},
{ID: 1, Email: "user2@example.com", Name: "user2"},
}
func GetUsers() []User {
return users
}
controller package
controllerフォルダーにcontrollerパッケージでuser.controller.goを作成します。
$ mkdir controller
$ vi controller/user.controller.go
package controller
import (
"net/http"
"example.com/example-gin/service"
"github.com/gin-gonic/gin"
)
func GetUsers(c *gin.Context) {
users := service.GetUsers()
c.JSON(http.StatusOK, users)
}
ハンドラーを含んだmain.go
$ vi main.go
package main
import (
"example.com/example-gin/controller"
"github.com/gin-gonic/gin"
)
func main() {
engine := gin.Default()
engine.GET("/user", controller.GetUsers)
engine.Run("localhost:3000")
}
実行してみる
では実行してみます。以下のように起動できましたらブラウザーを開いて”http://localhost:3000/user”を入力してみます
$ go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /user --> example.com/example-gin/controller.GetUsers (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on localhost:3000
他のAPIも追加してみましょう
特定(ID指定)ユーザー情報を取得
$ vi service/user.service.go
...
func GetUserByID(id int32) (User, error) {
for _, user := range users {
if user.ID == id {
return user, nil
}
}
return User{}, errors.New("User not found")
}
...
$ vi controller/user.controller.go
...
func GetUserByID(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
return
}
user, err := service.GetUserByID(int32(id))
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
return
}
c.JSON(http.StatusOK, user)
}
...
$ vi main.go
...
engine.GET("/user/:id", controller.GetUserByID)
...
ユーザー追加
$ vi service/user.service.go
...
func CreateUser(user User) {
users = append(users, user)
}
...
$ vi controller/user.controller.go
...
func CreateUser(c *gin.Context) {
var newUser service.User
if err := c.ShouldBindJSON(&newUser); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
service.CreateUser(newUser)
c.JSON(http.StatusCreated, newUser)
}
...
$ vi main.go
...
engine.POST("/user", controller.CreateUser)
...
ユーザー情報の更新
$ vi service/user.service.go
...
func UpdateUserByID(id int32, updateUser User) (User, error) {
for idx, user := range users {
if user.ID == id {
users[idx] = updateUser
return users[idx], nil
}
}
return User{}, errors.New("User not found")
}
...
$ vi controller/user.controller.go
...
func UpdateUserByID(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
return
}
var updatedUser service.User
if err := c.ShouldBindJSON(&updatedUser); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
user, err := service.UpdateUserByID(int32(id), updatedUser)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
return
}
c.JSON(http.StatusOK, user)
}
...
$ vi main.go
...
engine.PUT("/user/:id", controller.UpdateUserByID)
...