go-ginチュートリアル — Part2

GinのBlog API’s 作成 — Routers

gavin.zhou
11 min readNov 8, 2018

第1回の続き、今回Blog Api’sを作成します。dbとredisを使う予定です。環境が未構築なのであれば、前回の環境構築をご参照ください。

TL;DR

APIを作成前、下記のことを一緒に考えてほしい

  • 設定ファイルはプログラムの中に含まれる?
  • APIのエラーコードをプログラムにハードコードにする?
  • dbのアクセスが毎回db clientを初期化する?
  • 統一管理しないで、共有関数をばらばらにする?

やはり、ちゃんとしているプロジェクトであればこれではいけません。

最終的に、コンテナ上に稼働したいから、設定ファイルの管理を envconfig を使います。

簡単なAPIエラーコードを作成します。後ほど、詳細を説明します。

プロジェクトの初期化

最初、Goプロジェクトを管理しやすくするため、$GOPATHに workspace を作成します。ディレクトリは下記となります。

Workspace初期化

$GOPATH
├── bin
├── pkg
└── src
└── github.com
└──gavinzhou
└──hello-gin

Project Directory初期化

~ ❯❯❯ cd hello-gin
~ ❯❯❯ mkdir {conf,middleware,models,pkg,routers,runtime}
hello-gin/
├── middleware
├── models
├── pkg
├── routers
└── runtime
  • middleware:应用中间件
  • models:应用数据库模型
  • pkg:第三方包
  • routers 路由逻辑处理
  • runtime 应用运行时数据

環境変数から設定を取るため、 .env フィアルを作成します。

export RUN_MODE="debug"
export PAGESIZE=10
export JWTSECRET="23347$040412"
export PORT=8000
export READTIMEOUT=60s
export WRITETIMEOUT=60s
export USERNAME="gavin"
export PASSWORD=""
export DBHOST="127.0.0.1"
export DATABASE="blog"
export TABLE_PREFIX="blog_"

setting モジュールを作成します。 pkg フォルダに setting フォルダを作成し、 setting.go フィアルを作成します。コードが下記の内容となります。

Create api error code

エラーコードの e モジュールを作成します。 pkg フォルダに e フォルダを作成し、 code.gomsg.go ファイルを作ります。内容が下記となります。

Create util Tools

utilツールモジュールを作成します。pkg フォルダに utilフォルダを作成し、 pageination.go を作ります。下記のコードを記載します。

Create main.go & routers.go

最初の環境ファイルができたら、 Demo を作りましょう。hello-gin フォルダに main.go を作ります。

Run main.go

❯❯❯ eval $(cat .env)
❯❯❯ 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 /test --> main.main.func1 (3 handlers)

レスポンスを確認します。

❯❯❯ curl 127.0.0.1:8000/test                                                                                                                           
{"message":"test"}%

Topics

1、 標準ライブラリ:

  • fmt:Cの printf、scanfのフォーマットを実現します。Cの (‘verb’)式のコンセプトで作りました。更にシンプル化にしました。
  • net/http: http server&cleint の実現

2、 Gin:

  • gin.Default(): Gin type Engine struct{...}を返します。RouterGroupを含まれます。RouterのHandlersを作ります。routerルールや、ミドルウェア等を追加できます。
  • router.GET(…){…}:RouterのHTTP HandlersがGetメソッドを使います。 POST、PUT、DELETE、PATCH、OPTIONS、HEAD 等にも対応します。
  • gin.H{…}map[string]interface{} です。
  • gin.ContextginContextとなります。これにより、ミドルウェア間での変数の受け渡し、ストリームの管理、jsonのレスポンス検証、json 要求への応答などを行うことができます、一般的な DefaultQueryQueryDefaultPostFormPostForm等が含まれます。

3、 &http.ServerListenAndServe

http.Server

type Server struct {
Addr string
Handler Handler
TLSConfig *tls.Config
ReadTimeout time.Duration
ReadHeaderTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxHeaderBytes int
ConnState func(net.Conn, ConnState)
ErrorLog *log.Logger
}
  • ADDR: モニターの TCP アドレスとしてフォーマット: 8000
  • Handler: http 要求に応答するHandlerの http Handler(基本的には servehttp)
  • Tlsconfig: セキュアトランスポート層プロトコル (TLS) の構成
  • ReadTimeout: 読み取りに許容される最大時間
  • Readheadertimeout: 要求ヘッダーの読み取りを許可される最大時間
  • WriteTimeout: 書き込みに許容される最大時間
  • IdleTimeout: 最大待機時間
  • Maxheaderbytes: 要求ヘッダーの最大バイト数
  • Connstate: クライアント接続が変更されたときに呼び出されるオプションのコールバック関数を指定します。
  • Errorlog: 受信側のプログラムの予期しない動作および基本になるシステムエラーのためのオプションのロガーを指定します。設定されていない場合、または nil の場合は、デフォルトでログパッケージの標準ロガー (コンソールでの出力) が実行されます。

ListenAndServe

func (srv *Server) ListenAndServe() error {
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}

リッスンサービスを開始し、TCP ネットワークアドレス addr をリッスンし、アプリケーションを呼び出して接続に対する要求を処理します。

Addr が私たち設定した&http.Serverの関数を使います。そして、設定する時に、 &を使います。 &http.Server内のパラメータは、お互いに影響します。

4、なぜ Demo の時 Warning を出てきた

最初から、 Default() を見てみましょう。

// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery())
return engine
}

Defaultでログとリカバリーを使います。最初debugPrintWARNINGDefault() の部は Warning の出力です。

func debugPrintWARNINGDefault() {
debugPrint(`[WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
`)
}

.envファイルの RUN_MODEをreleaseに変更すれば、 Warningがなくなります。

Running in "debug" mode. Switch to "release" mode in production.

やはり、 routersmain.go に含まれると良くない。 専用な routers に移動しましょう。

gin-blogroutersフォルダにrouter.goファイルを作成します。

package routers

import (
"github.com/gin-gonic/gin"

"github.com/gavinzhou/hello-gin/pkg/setting"
)

func InitRouter(config setting.AppConfig) *gin.Engine {
r := gin.New()

r.Use(gin.Logger())

r.Use(gin.Recovery())

gin.SetMode(config.RunMode)

r.GET("/test", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "test",
})
})

return r
}

main.go を下記のように修正します。

package main

import (
"fmt"
"log"
"net/http"

"github.com/gavinzhou/hello-gin/pkg/setting"
"github.com/gavinzhou/hello-gin/routers"
)

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()
}

フォルダ構成が下記となります。

.
├── README.md
├── conf
├── main.go
├── middleware
├── models
├── pkg
│ ├── e
│ │ ├── code.go
│ │ └── msg.go
│ ├── setting
│ │ └── setting.go
│ └── util
│ └── pageination.go
├── routers
│ └── routers.go
└── runtime

Restart Server

❯❯❯ 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 /test --> github.com/gavinzhou/hello-gin/routers.InitRouter.func1 (3 handlers)

アクセステスト、前回と同じ結果です。

❯❯❯ curl 127.0.0.1:8000/test
{"message":"test"}%

参考サイト

※問題があった場合は、githubの issue をいただければ、幸いです。

--

--

Responses (26)