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.go
と msg.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.Context:
gin
のContext
となります。これにより、ミドルウェア間での変数の受け渡し、ストリームの管理、jsonのレスポンス検証、json 要求への応答などを行うことができます、一般的なDefaultQuery
、Query
、DefaultPostForm
、PostForm
等が含まれます。
3、 &http.Server
和ListenAndServe
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.
やはり、 routers
が main.go
に含まれると良くない。 専用な routers
に移動しましょう。
gin-blog
のrouters
フォルダに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
をいただければ、幸いです。