Golang web starter
背景
Web應用長期以來是Ruby、Java、PHP等開發語言的戰場。
- Ruby可以實現快速原型開發,Ruby On Rails “全能”框架實現“全棧”開發,缺點有大型應用效能差、除錯困難;
- Java 20多年的發展歷程,各種第三方庫、框架健全,執行效率高,但是隨著應用的功能膨脹,臃腫的get/set方法,JVM佔用大量計算機資源、效能除錯困難,函數語言程式設計不友好。
- PHP,TL;DR
本文實現了一個最小化web應用,以此來了解Golang web的生態,通過使用Docker隔離開發環境,使用Posgres持久化資料,原始碼請參考這裡
Why Go?
- 效能優越
- 部署簡單,只需要將打包好的二進位制檔案部署到伺服器上
- 內建豐富的標準庫,讓程式設計師的生活變得簡單美好
- 靜態語言,型別檢查
- duck typing
- goroutine將開發人員從併發程式設計中解放出來
- 函式作為“一等公民”
- ...
Golang第三方框架選擇
- Web框架: Gin,效能卓越,API友好,功能完善
- ORM: GORM,支援多種主流資料庫方言,文件清晰
- 包管理工具: Glide,類似於Ruby的bundler或者NodeJS中的npm
- 測試工具:
- 資料庫migration: migrate
- 日誌工具: Logrus,結構化日誌輸出,完全相容標準庫的logger
Dockerize開發環境
釋出應用base image
Dockerfile如下:
FROM golang:1.8
# 包管理工具
RUN curl https://glide.sh/get | sh
# 程式碼熱載入
RUN go get github.com/codegangsta/gin
# 資料庫migration工具
RUN go get -u -d github.com/mattes/migrate/cli github.com/lib/pq
RUN go build -tags 'postgres' -o /usr/local/bin/migrate github.com/mattes/migrate/cli
釋出資料庫base image
Dockerfile如下:
FROM postgres:9.6
# 初始化資料庫配置
COPY ./init-user-db.sh /docker-entrypoint-initdb.d/init-user-db.sh
啟動服務
執行auto/dev
即可啟動,具體的配置如下。
- docker-compose.yml:
version: "3"
services:
dev:
links:
- db
image: 415148673/golang-web-base-image@sha256:18de5eb058a54b64f32d58b57a1eb3009b9ed49d90bd53056b95c5c8d5894cd6
environment:
- PORT=8080
- DB_USER=docker
- DB_HOST=db
- DB_NAME=webstarter
volumes:
- .:/go/src/golang-web-starter
working_dir: /go/src/golang-web-starter
ports:
- "3000:3000"
command: gin
db:
image: 415148673/postgres@sha256:6d4800c53e68576e05d3a61f2b62ed573f40692bcc72a3ebef3b04b3986bb70c
volumes:
- go-web-starter-db-cache:/var/lib/postgresql/data
volumes:
go-web-starter-db-cache:
- 安裝第三方依賴所需的glide配置檔案,通過在容器內執行
glide install
進行安裝:
package: golang-web-starter
import:
- package: github.com/gin-gonic/gin
version: ^1.1.4
- package: github.com/jinzhu/gorm
version: ^1.0.0
- package: github.com/mattes/migrate
version: ^3.0.1
- package: github.com/lib/pq
- package: github.com/stretchr/testify
version: ^1.1.4
- package: github.com/smartystreets/goconvey
version: ^1.6.2
- 資料庫migration的指令碼:
migrate -source file://migrations -database "postgres://$DB_USER:$DB_PASSWORD@$DB_HOST:5432/$DB_NAME?sslmode=disable" up
業務實現
Router
router := gin.Default()
router.GET("/", handler.ShowIndexPage) // 顯示主頁面
router.GET("/book/:book_id", handler.GetBook) // 通過id查詢書籍
router.POST("/book", handler.SaveBook) // 儲存書籍
handler
以儲存書籍為例:
func SaveBook(c *gin.Context) {
var book models.Book
if err := c.Bind(&book); err == nil {
// 呼叫model的儲存方法
book := models.SaveBook(book)
// 繫結前端頁面所需資料
utility.Render(
c,
gin.H{
"title": "Save",
"payload": book,
},
"success.html",
)
} else {
// 異常處理
c.AbortWithError(http.StatusBadRequest, err)
}
}
model
func SaveBook(book Book) Book {
// 持久化資料
utility.DB().Create(&book)
return book;
}
建立DB連線
func DB() *gorm.DB {
dbInfo := fmt.Sprintf(
"host=%s user=%s dbname=%s sslmode=disable password=%s",
os.Getenv("DB_HOST"),
os.Getenv("DB_USER"),
os.Getenv("DB_NAME"),
os.Getenv("DB_PASSWORD"),
)
db, err := gorm.Open("postgres", dbInfo)
if err != nil {
log.Fatal(err)
}
return db
}
View
<body class="container">
{{ template "menu.html" . }}
<label>儲存成功</label>
<h1>{{.payload.Title}}</h1>
<p>{{.payload.Author}}</p>
{{ template "footer.html" .}}
</body>
測試
func TestSaveBook(t *testing.T) {
r := utility.GetRouter(true)
r.POST("/book", SaveBook)
Convey("The params can not convert to model book", t, func() {
req, _ := http.NewRequest("POST", "/book", nil)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
utility.TestHTTPResponse(r, req, func(w *httptest.ResponseRecorder) {
So(w.Code, ShouldEqual, http.StatusBadRequest)
})
})
Convey("The params can convert to model book", t, func() {
req, _ := http.NewRequest("POST", "/book", strings.NewReader("title=Hello world&author=will"))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
utility.TestHTTPResponse(r, req, func(w *httptest.ResponseRecorder) {
p, _ := ioutil.ReadAll(w.Body)
So(w.Code, ShouldEqual, http.StatusOK)
So(string(p), ShouldContainSubstring, "儲存成功")
})
})
}
總結
Go生態之活躍令我大開眼界,著名的應用如ocker, Ethereum都是使用Go編寫的。使用Go進行web開發的過程,感覺和搭積木一樣,一個合適的第三方庫需要在多個候選庫中精心篩選,眾多的開源作者共同構建了一個“模組”王國。在這樣的環境中,程式設計變成了一件很自由的事情。由於Go的標準庫提供了很多內建的實用命令如go fmt
,go test
,讓程式設計變得異常輕鬆,簡直是強迫型程式設計師的“天堂”。
當然Go語言還處在發展過程中,也有許多不完善的地方,比如
- 缺少標準的依賴管理工具(正在開發的
dep
) - 非中心化的依賴倉庫會出現由於某個依賴被刪除導致應用不可用等。
歡迎關注我的微信公眾平臺,更多隨筆隨後更新: whisperd
相關文章
- golang web框架,golang版本laravel 框架GolangWeb框架Laravel
- golang web框架——gin使用教程(一)GolangWeb框架
- 自研 Pulsar Starter:winfun-pulsar-spring-boot-starterSpringboot
- Golang web filter 輕量級實現GolangWebFilter
- GOLANG Web請求引數驗證GolangWeb
- 【雜談】Starter Template
- SpringBoot 自定義 starterSpring Boot
- SpringBoot如何建立starterSpring Boot
- SpringBoot自定義StarterSpring Boot
- 基於gin的golang web開發:路由GolangWeb路由
- 基於gin的golang web開發:dockerGolangWebDocker
- 自定義 Spring Boot StarterSpring Boot
- Spring Boot 自定義 starterSpring Boot
- Shooter Game User Interface StarterGAM
- spring-boot-starter-testSpringboot
- 自定義Spring Boot StarterSpring Boot
- Golang中的強大Web框架Fiber詳解GolangWeb框架
- 基於gin的golang web開發:整合swaggerGolangWebSwagger
- 基於gin的golang web開發:路由二GolangWeb路由
- Go For Web:Golang http 包詳解(原始碼剖析)WebGolangHTTP原始碼
- 【spring-boot】自定義starterSpringboot
- 理解spring-boot-starter-parentSpringboot
- Spring Boot Starter 和 ABP ModuleSpring Boot
- Golang初學:新建專案 及 gin web 專案GolangWeb
- 基於gin的golang web開發:模型驗證GolangWeb模型
- 基於gin的golang web開發:模型繫結GolangWeb模型
- 基於gin的golang web開發:中介軟體GolangWeb
- 使用Golang語言編寫Hello World Web應用GolangWeb
- 使用Golang的Gin框架和vue編寫web應用Golang框架VueWeb
- [NVIDIA英偉達] Senior Web Full Stack Engineer (Golang)工程師WebGolang工程師
- Golang 入門系列(十三)用Beego開發web應用GolangWeb
- 【北京】美餐招聘 Golang、Web 前端工程師(待遇你自己看)GolangWeb前端工程師
- 基於gin的golang web開發:認證利器jwtGolangWebJWT
- 基於gin的golang web開發:服務間呼叫GolangWeb
- 聊聊arthas的spring-boot-starterSpringboot
- 開發一個Spring Boot Starter!Spring Boot
- Springboot Starter的核心實現原理Spring Boot
- 實戰|如何自定義SpringBoot Starter?Spring Boot
- 手擼一個SpringBoot-StarterSpring Boot