go-ginチュートリアル — Part3
GinのBlog API’s 作成 — DB
第2回の続き、DB周りのモジュールを作ります。サンプルコードがこちらでご参照ください。githubのtagは part03
となります。
TL;DR
Setting 改善
DBモジュールを作る前、改めて main.go
のコードをみてみましょ。
...
func main() {
config, err := setting.NewConfig()
if err != nil {
log.Fatal("Can init Config with Environment: %v", err)
}
router := routers.InitRouter(config.AppConfig)
s := &http.Server{
Addr: fmt.Sprintf(":%d", config.HTTPPort),
Handler: router,
ReadTimeout: config.ReadTimeout,
WriteTimeout: config.WriteTimeout,
MaxHeaderBytes: 1 << 20,
}
s.ListenAndServe()
}
このconfig, err := setting.NewConfig()
部分です、 setting
の設定を関数の中に使うものですから、 init
に変更します。
package setting
import (
"log"
"time"
"github.com/kelseyhightower/envconfig"
)
type DBConfig struct {
DBType string `envconfig:"DBType"`
DBHost string `envconfig:"DBHost"`
DBPort string `envconfig:"DBPort"`
DBUser string `envconfig:"DBUser"`
DBPassword string `envconfig:"DBPassword"`
DBName string `envconfig:"DBName"`
TablePrefix string `envconfig:"TABLE_PREFIX"`
}
type ServerConfig struct {
HTTPPort int `envconfig:"PORT"`
ReadTimeout time.Duration `envconfig:"READTIMEOUT"`
WriteTimeout time.Duration `envconfig:"WRITETIMEOUT"`
}
type AppConfig struct {
RunMode string `envconfig:"RUN_MODE"`
PageSize int `envconfig:"PAGESIZE"`
JwtSecret string `envconfig:"JWTSECRET`
}
type config struct {
DBConfig
ServerConfig
AppConfig
}
var Config = config{}
func init() {
err := envconfig.Process("", &Config)
if err != nil {
log.Fatalf("Fail to load config wiht env : %v", err)
}
}
main.go
ファイルを修正します。
package main
import (
"fmt"
"net/http"
"github.com/gavinzhou/hello-gin/pkg/setting"
"github.com/gavinzhou/hello-gin/routers"
)
func main() {
config := setting.Config.ServerConfig
router := routers.InitRouter()
s := &http.Server{
Addr: fmt.Sprintf(":%d", config.HTTPPort),
Handler: router,
ReadTimeout: config.ReadTimeout,
WriteTimeout: config.WriteTimeout,
MaxHeaderBytes: 1 << 20,
}
s.ListenAndServe()
}
pageination.go
にも修正が必要です。
package util
import (
"github.com/Unknwon/com"
"github.com/gin-gonic/gin"
"github.com/gavinzhou/hello-gin/pkg/setting"
)
func GetPage(c *gin.Context) int {
result := 0
page, _ := com.StrTo(c.Query("page")).Int()
if page > 0 {
result = (page - 1) * setting.Config.PageSize
}
return result
}
第2回に書いたテスト方法と同じ、テストしてみてください。
これから、 setting
の関数を別の関数の中にも使えます。
新規Routersを追加
Blogはよく便利の機能はTagですから、最初Tagの部分を作ります。
いつもコードを書く前、機能の部分を整理しましょ。検索、追加、削除、修正の部分があります。下記のような定義します!
- Get Tagリスト:
GET("/tags")
- 新規Tag追加:
POST("/tags")
- 指定Tag更新:
PUT("/tags/:id")
- 指定Tag削除:
DELETE("/tags/:id")
routers
フォルダに新規api
フォルダを追加します、今回のAPIは最初 v1
として、名前は v1
のフォルダを作ります。その v1
フォルダにtag.go
フィアルを作ります。下記のコード通り。
package v1
import (
"github.com/gin-gonic/gin"
)
func GetTags(c *gin.Context) {
}
func AddTag(c *gin.Context) {
}
func EditTag(c *gin.Context) {
}
func DeleteTag(c *gin.Context) {
}
routers.go
に新規作った v1
を登録します。
package routers
import (
"github.com/gin-gonic/gin"
"github.com/gavinzhou/hello-gin/pkg/setting"
"github.com/gavinzhou/hello-gin/routers/api/v1"
)
func InitRouter() *gin.Engine {
r := gin.New()
r.Use(gin.Logger())
r.Use(gin.Recovery())
gin.SetMode(setting.RunMode)
apiv1 := r.Group("/api/v1")
{
apiv1.GET("/tags", v1.GetTags)
apiv1.POST("/tags", v1.AddTag)
apiv1.PUT("/tags/:id", v1.EditTag)
apiv1.DELETE("/tags/:id", v1.DeleteTag)
}
return r
}
プロジェクトディレクトリが下記の形になりました。
.
├── README.md
├── conf
├── main.go
├── middleware
├── models
├── pkg
│ ├── e
│ │ ├── code.go
│ │ └── msg.go
│ ├── setting
│ │ └── setting.go
│ └── util
│ └── pageination.go
├── routers
│ ├── api
│ │ └── v1
│ │ └── tag.go
│ └── routers.go
└── runtime
新規追加 v1
Routerを確認します。下記のような追加したRouterを表示します。
❯❯❯ eval $(cat .env)
❯❯❯ go run main.go
[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 /api/v1/tags --> github.com/gavinzhou/hello-gin/routers/api/v1.GetTags (3 handlers)
[GIN-debug] POST /api/v1/tags --> github.com/gavinzhou/hello-gin/routers/api/v1.AddTag (3 handlers)
[GIN-debug] PUT /api/v1/tags/:id --> github.com/gavinzhou/hello-gin/routers/api/v1.EditTag (3 handlers)
[GIN-debug] DELETE /api/v1/tags/:id --> github.com/gavinzhou/hello-gin/routers/api/v1.DeleteTag (3 handlers)
問題がなければ、 v1
Routersを書きましょ。
DB初期化
postgresqlを使います。 brew
がおすすめです。
~ ❯❯❯ brew install postgresql
~ ❯❯❯ brew services start postgresql
~ ❯❯❯ createdb blog -O gavin -E UTF8 -e
Create tag
Table
CREATE SEQUENCE blog_tag_seq;CREATE TABLE blog_tag (
id int check (id > 0) NOT NULL DEFAULT NEXTVAL ('blog_tag_seq'),
name varchar(100) DEFAULT '' ,
created_on int check (created_on > 0) DEFAULT '0' ,
created_by varchar(100) DEFAULT '' ,
modified_on int check (modified_on > 0) DEFAULT '0' ,
modified_by varchar(100) DEFAULT '' ,
state smallint check (state > 0) DEFAULT '1' ,
PRIMARY KEY (id)
) ;
Create article
Table
CREATE SEQUENCE blog_article_seq;CREATE TABLE blog_article (
id int check (id > 0) NOT NULL DEFAULT NEXTVAL ('blog_article_seq'),
tag_id int check (tag_id > 0) DEFAULT '0' ,
title varchar(100) DEFAULT '' ,
description varchar(255) DEFAULT '',
content text,
created_on int DEFAULT NULL,
created_by varchar(100) DEFAULT '' ,
modified_on int check (modified_on > 0) DEFAULT '0' ,
modified_by varchar(255) DEFAULT '' ,
state smallint check (state > 0) DEFAULT '1' ,
PRIMARY KEY (id)
);
Create auth
Table
CREATE SEQUENCE blog_auth_seq;CREATE TABLE blog_auth (
id int check (id > 0) NOT NULL DEFAULT NEXTVAL ('blog_auth_seq'),
username varchar(50) DEFAULT '' ,
password varchar(50) DEFAULT '' ,
PRIMARY KEY (id)
);
Insert auth
User
INSERT INTO blog_auth (username, password) VALUES ('test', 'test123456');
Create models
init
DB周りは gorm
を使います、最初パッケージをダウンロードします。
go get -u github.com/jinzhu/gorm
postgresを使うため、 postgres
のdriverにも
go get -u github.com/go-sql-driver/postgres
gin-blog
のmodels
フォルダにmodels.go
ファイルを作ります。models
の初期化を使います。
続き models
フォルダに tag.go
ファイルを作ります。内容が下記の通ります。
Topic
Gorm
用のTag struct{}
を定義しました。json
を付けるとc.JSON
の時、使います。- 返す関数を定義したため、そのまま
return
を使います。 db
がどこに定義したか?同じmodels
パッケージに定義した、db *gorm.DB
をそのまま使えます。
Cteate Router
routers
フォルダのv1
バージョンの tag.go
にGetTags
を実装します。
package v1
import (
"net/http"
"github.com/Unknwon/com"
"github.com/gavinzhou/hello-gin/models"
"github.com/gavinzhou/hello-gin/pkg/e"
"github.com/gavinzhou/hello-gin/pkg/setting"
"github.com/gavinzhou/hello-gin/pkg/util"
"github.com/gin-gonic/gin"
)
func GetTags(c *gin.Context) {
name := c.Query("name")
maps := make(map[string]interface{})
data := make(map[string]interface{})
if name != "" {
maps["name"] = name
}
var state int = -1
if arg := c.Query("state"); arg != "" {
state = com.StrTo(arg).MustInt()
maps["state"] = state
}
code := e.SUCCESS
data["lists"] = models.GetTags(util.GetPage(c), setting.Config.PageSize, maps)
data["total"] = models.GetTagTotal(maps)
c.JSON(http.StatusOK, gin.H{
"code": code,
"msg": e.GetMsg(code),
"data": data,
})
}
func AddTag(c *gin.Context) {
}
func EditTag(c *gin.Context) {
}
func DeleteTag(c *gin.Context) {
}
Topic
c.Query
は?name=test&state=1
のクエリーを取得できます。c.DefaultQuery
のディフォルト設定値があります。code
変数は最初e
のエラーコードを使います。エラーコードが最初から定義するとエラーコードが管理しやすくなります。util.GetPage
は各page
処理が統一します。
Test
Run Server
❯❯❯ eval $(cat .env)
❯❯❯ go run *.go
[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 /api/v1/tags --> github.com/gavinzhou/hello-gin/routers/api/v1.GetTags (3 handlers)
[GIN-debug] POST /api/v1/tags --> github.com/gavinzhou/hello-gin/routers/api/v1.AddTag (3 handlers)
[GIN-debug] PUT /api/v1/tags/:id --> github.com/gavinzhou/hello-gin/routers/api/v1.EditTag (3 handlers)
[GIN-debug] DELETE /api/v1/tags/:id --> github.com/gavinzhou/hello-gin/routers/api/v1.DeleteTag (3 handlers)
tag
を取得してみます。正常なら、下記の様な感じですが、エラーの場合は gin
のエラーメッセージで直してください。
❯❯❯ curl 127.0.0.1:8000/api/v1/tags
{"code":200,"data":{"lists":[],"total":0},"msg":"ok"}