- 本機安裝Golang
- IDE設定
- VS Code
- 基本語法略懂
- go tool基本指令
- go get
- go build
- go install
- 套件管理
- govendor
- gin
- 基本server
- server file structure
- import / export
- DB操作
- JWT
- How to Sign
- How to Verify
- Use JWT to Auth
- Cookie
- Get Cookie by key name
- Set Cookie
- How to write a middleware
- 參數
- go flag
- viper
- Security
- Use Regex filter forbidden chars
- secure http headers
- Log file
- Live Reload on Save
- Coding Style
- editorconfig
- gofomat
- 測試
- Unit test
- API test
- Dockerize
Golang的所有程式,都會統一放在一個WorkSpace裡,
環境變數$GOPATH必需指向這個WorkSpace
WorkSpace會有三個資料夾bin
, pkg
與src
透過go get
下載套件時,會同時將套件的source code放置在src中
如果希望像Node.js一樣,Project管理各自的套件
要將套件存放在各Project下的vendor
資料夾
/$GOPATH
|-----/bin
|-----/pkg
|-----/src
| |----/Project1
| | |-------/vendor
| | |-------main.go
�在node.js中我們使用npm作套件管理,在golang中,還沒有像npm一樣,官方的工具
這個案子中選用的是govendor
,各工具比較可以參閱 go依赖包管理工具对比
go get -u github.com/kardianos/govendor
- 在Project執行init
類似 npm init或yarn init
govendor init
init後會產生vendor資料夾跟vendor.json檔
- 下載指定套件,並記錄到vendor.json
類似 npm i 或 yarn add
govendor fetch <套件URL>
- 以vendor.json下載套件
類似 npm 或 yarn
govendor sync
vendor/*/
- Project/vendor
- 向上層目錄找,直到找到src/vendor
- $GOPATH
- $GOROOT
govendor fetch github.com/gin-gonic/gin
新增main.go
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run() // listen and serve on 0.0.0.0:8080
}
執行
go run main.go
再以browser連到localhost:8080/ping,若有拿到pong代表server建立成功
參考下面這篇文章 https://github.com/josephspurrier/gowebapp
在Project Root有一個entry point main.go
其他都放在app目錄中,以import的方式匯入
https://peter.bourgon.org/go-best-practices-2016/ 上文的Best Practice中提到
Always use fully-qualified import paths. Never use relative imports.
為了讓程式中可以import子資料夾下的go檔,寫的時候必需以完整路徑,不建議用./
有些Project會把source code放到vendor下,但vendor的用途類似node_modules,應該只放3rd prarty的lib
不建議這樣放
import app/shared/secure // 不好
import devpack.cc/ginboilerplate/app/shared/secure //好
資料夾結構如下
/$GOPATH
|-----/bin
|-----/pkg
|-----/src
| |----/Project1
| | |-----/app
| | | |-----/controller
| | | |-----/route
| | | |-----/model
| | | |-----/shared
| | | |-----/secure
| | |-------/vendor
| | |-------main.go
vendor下除了govendor fetch下來的packages外,app目錄下是所有的source codea
使用的是 https://github.com/unrolled/secure
請參照 vendor/app/shared/secure/secure.go
下面的code完全從gin官網照抄
func main() {
// Disable Console Color, you don't need console color when writing the logs to file.
gin.DisableConsoleColor()
// Logging to a file.
f, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f)
// Use the following code if you need to write the logs to file and console at the same time.
// gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
r.Run(":8080")
}
reference the code below, to generate self-sign ssl key
mkdir -p certs
openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/domain.key \
-subj '/C=TW/ST=Taiwan/L=Taipei/O=Wistron/OU=SWPC/CN=gogoge/' \
-x509 -days 365 -out certs/domain.crt
原理是用[go statement](https://golang.org/ref/spec#Go_statements)
以concurrent的方式同時開HTTP及HTTPs兩台server
再設定HTTP Header的SSLRedirect
把非HTTPS的request都redirect到HTTPS server
若 HTTP port 80, HTTPs port 443 連80還是只能HTTP 但用HTTP連443會自動轉HTTPs
https://github.com/cosmtrek/air
curl -fLo $GOPATH/bin/air \
https://raw.githubusercontent.com/cosmtrek/air/master/bin/darwin/air
chmod +x $GOPATH/bin/air
啟動server
air
fresh air is inspire from fresh
gin
Live code reloading for Golang web projects in 19 lines
func DummyMiddleware(c *gin.Context) {
fmt.Println("Im a dummy!")
// Pass on to the next-in-chain
c.Next()
}
func main() {
// Insert this middleware definition before any routes
api.Use(DummyMiddleware)
// ... more code
}
Read more:
http://sosedoff.com/2014/12/21/gin-middleware.html
因為我們需要在JWT寫入自定義的內容,所以使用custom claims
https://godoc.org/github.com/dgrijalva/jwt-go#ex-NewWithClaims--CustomClaimsType
https://godoc.org/github.com/dgrijalva/jwt-go#ex-ParseWithClaims--CustomClaimsType
func readCookie(c *gin.Context) {
return c.Cookie(<cookie key name>)
}
https://godoc.org/github.com/gin-gonic/gin#Context.Cookie
func setCookie(c *gin.Context) {
// name string
// value string
// maxAge int
// path string
// domain string
// secure bool
// httpOnly bool
return c.SetCookie(name, value, maxAgent, path, domain, secure, httpOnly)
}
https://godoc.org/github.com/gin-gonic/gin#Context.SetCookie
https://tour.golang.org/list
https://golang.org/doc/code.html#Organization
https://golang.org/doc/effective_go.html#introduction
https://github.com/alco/gostart
https://gobyexample.com
https://golangbot.com