【Go】始め-ウェブサーバー構築

Go Go

概要

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)
...
タイトルとURLをコピーしました