本文不一樣於其餘Go語言學習的文章,將以項目開發所需爲基礎,帶你飛速踏入Go的世界,成爲能獨擋一面的強者。當你稍微花幾分鐘的時間,看完本文的時候,或許你會發現,駕馭Go語言爲己所用是如此簡單。
山不在高,有仙則名,水不在深,有龍則靈,文不在多,一篇足以。但願我這些小小的經驗和用心的分享,能真的幫助到您。css1、Go 語言簡介
1 Go 語言介紹
Go 即Golang,是Google公司2009年11月正式對外公開的一門編程語言。
Go是靜態強類型語言,是區別於解析型語言的編譯型語言。mysql解析型語言——源代碼是先翻譯爲中間代碼,而後由解析器對代碼進行解釋執行。
編譯型語言——源代碼編譯生成機器語言,而後由機器直接執行機器碼便可執行。linux2 Go語言特性
- 跨平臺的編譯型語言
- 語法接近C語言
- 管道(channel),切片(slice),併發(routine)
- 有垃圾回收的機制
- 支持面向對象和麪向過程的編程模式
3 Go 語言特點
- 編程模式比較簡單,沒有複雜的設計模式
- 所有源碼編譯到一個文件,編譯速度很快
- 最新版本也有動態庫形式,對跨語言調用的支撐更到位
- 開源框架比較成熟,新崛起的互聯網公司都在用
- 如滴滴,uber,百度,阿里巴巴,oppo,vivo等
- 微服務的開發模式下Go語言是新寵
4 Go 擅長領域
- 服務開發,web的api開發,分佈式服務集羣的開發
- 容器docker是go開源的產品,k8s等這些都是基於go語言的
- 對高併發、高性能的系統和服務支撐,Go語言對比其餘語言有更快的開發速度,更高的開發效率
- 獨有的語言特性和設計模式routine,channel,sync包支撐了海量並行的支持。
因此能看到這些領域都在使用Go語言:微服務開發模式,api開發,rpc服務開發,遊戲服務開發等等git
2、框架選擇
Go的開發框架比較多,比較知名的幾個分別是Gin、BeeGo、Iris、Echo、Revel、Buffalo。對比排名詳見: 《Go語言Web框架對比》github
框架的選擇上,本人主要遵循以下幾點原則,分別是:golang
- star多
- 易上手
- 性能佳
- 持續維護
選擇過程很少說,本人最終選擇了beego做爲本次入手的框架,本文餘下內容如無特別說明,均基於此框架。web
3、環境部署
1 Go 語言環境安裝
安裝包下載地址爲:https://golang.org/dl/
若是打不開可使用這個地址:https://golang.google.cn/dl/redis【UNIX/Linux/Mac OS X, 和 FreeBSD 安裝】sql
- 下載二進制包:go1.4.linux-amd64.tar.gz
- 將下載的二進制包解壓至 /usr/local目錄
tar-C/usr/local-xzf go1.12.7.linux-amd64.tar.gz
- 將 /usr/local/go/bin 目錄添加至PATH環境變量:
exportPATH=$PATH:/usr/local/go/bin
注意:MAC 系統下你可使用 .pkg 結尾的安裝包直接雙擊來完成安裝,安裝目錄在 /usr/local/go/ 下。
本文餘下內容如無特別說明,均默認Linux環境docker【Windows 系統下安裝】
Windows 下可使用 .msi 後綴(在下載列表中能夠找到該文件,如go1.12.7.windows-amd64.msi
)的安裝包來安裝。
默認狀況下.msi文件會安裝在c:\Go
目錄下。你能夠將c:\Go\bin
目錄添加到 PATH 環境變量中。
添加後你須要重啓命令窗口才能生效。安裝測試:
建立工做目錄C:\>Go_WorkSpace
。//test.go 文件代碼: package main import "fmt" func main() { fmt.Println("Hello, World!") }
使用 go 命令執行以上代碼輸出結果以下:
C:\Go_WorkSpace>go run test.go Hello, World!
2 Go 語言環境變量
以下環境變量,都是Go編譯和運行時必要的,若是安裝時沒有自動設置好,請務必本身手動添加。
- GOROOT: Go 安裝後的根目錄(例如:/usr/local/go)
- GOPATH: Go 的工做空間,就是咱們的開發和依賴包的目錄(例如:~/go)
添加環境變量方法:
- 命令行直接export(對當前終端有效)
- 將export加到
~/.bashrc
文件裏(對當前用戶有效,首次添加後記得source~/.bashrc
一下)export GOPATH=~/go export GOROOT=/usr/local/go
3 BeeGo 框架
【安裝】
安裝方式很是簡單,只要敲擊以下命令便可(bee工具能夠快速建立項目和自動監測運行,記得要安裝哦):go get github.com/astaxie/beego # bee 工具 go get github.com/beego/bee
安裝完以後,bee 可執行文件默認存放在
$GOPATH/bin
裏面,因此您須要把$GOPATH/bin
添加到您的環境變量PATH中:exportPATH=$PATH:$GOPATH/bin
常見問題:
- git 沒有安裝,請自行安裝不一樣平臺的 git,如何安裝請看《Git官方安裝說明》。
- git https 沒法獲取,請配置本地的 git,關閉 https 驗證:
git config--globalhttp.sslVerifyfalse
【建立項目】
打開終端,進入$GOPATH/src
所在的目錄,用bee工具建立一個項目:beenewfirstPro
【目錄結構】
這是一個典型的 MVC 架構的應用,main.go 是入口文件。目錄結構以下所示:. ├── conf │ └── app.conf ├── controllers │ └── default.go ├── main.go ├── models ├── routers │ └── router.go ├── static │ ├── css │ ├── img │ └── js │ └── reload.min.js ├── tests │ └── default_test.go └── views └── index.tpl 10 directories, 7 files
【編譯運行】
cd $GOPATH/src/firstPro
進入咱們建立的項目,使用bee run
來運行該項目,這樣咱們的應用就在 8080 端口(beego 的默認端口)跑起來了,讓咱們打開瀏覽器看看效果吧:4 命令使用
Go指令
- 下載:
goget
- 編譯:
go build
- 運行:
go run
Bee工具
- 新建項目:
bee api
或beenew
分別建立 api應用 和 web項目,位於$GOPATH/src
目錄下- 運行:
bee run
該命令必須在$GOPATH/src/appname
下執行- 打包:
bee pack
以上是一些經常使用的命令,介紹雖比較簡單,但只要大概記住他的做用便可。
沒有把全部指令都列出來,由於咱們暫時還用不到,這些已經足夠咱們擺平項目的開發了,其餘的指令和高級用法,有興趣的小夥伴們能夠動動手自行查閱。4、Go 基礎語法
1 變量
Go 語言變量名由字母、數字、下劃線組成,其中首個字符不能爲數字。
【聲明變量】
- 使用 var 關鍵字:
varname type
- 一次聲明多個變量:
varname1,name2 type
- 省略var形式:
name:=value
【零值】
指定變量類型,若是沒有初始化,則變量默認爲零值。
- 數值類型(包括complex64/128)爲 0
- 布爾類型爲 false
- 字符串爲 ""(空字符串)
- 如下幾種類型爲 nil:
var a *int var a []int var a map[string] int var a chan int var a func(string) int var a error // error 是接口
【示例】
package main import "fmt" func main() { var a string = "yisonli" fmt.Println(a) var b, c int = 1, 2 fmt.Println(b, c) d := []int{10,9,8,7} fmt.Println(d) var e = map[string]int{"one":1, "two":2} fmt.Println(e) }
以上示例輸出結果爲:
yisonli 1 2 [10 9 8 7] map[one:1 two:2]
2 數據類型
類型 描述 布爾型 布爾型的值只能夠是常量 true
或者false
一個簡單的例子:varbbool=true
數字類型 有符號整型: int、int八、int1六、int3二、int64
無符號整型: uint、uint八、uint1六、uint3二、uint64
浮點型: float3二、float64
複數: complex6四、complex128
其餘: byte (相似uint8)、uintptr (存指針)、rune (相似int32)字符串類型 一串固定長度的字符鏈接起來的字符序列,使用 UTF-8 編碼標識 Unicode 文本 派生類型 包括:
(a) 指針類型(Pointer)
(b) 數組類型
(c) 結構化類型(struct)
(d) Channel 類型
(e) 函數類型
(f) 切片類型
(g) 接口類型(interface)
(h) Map 類型3 函數
Go 語言函數定義格式以下:
func function_name( [parameter list] ) [return_types] { 函數體 }
函數定義解析:
- func:函數聲明關鍵字
- function_name:函數名稱
- parameter list:參數列表
- 參數就像一個佔位符,當函數被調用時,你能夠將值傳遞給參數,這個值被稱爲實際參數。
- 參數列表指定的是參數類型、順序、及參數個數。
- 參數是可選的,也就是說函數也能夠不包含參數。
- return_types:返回類型
- 函數返回一列值, return_types 是該列值的數據類型。
- 有些功能不須要返回值,這種狀況下 return_types 不是必須的。
- 函數體:具體的代碼邏輯
【示例】
package main import "fmt" func swap(x, y string) (string, string) { return y, x } func main() { a, b := swap("Golang", "yisonli") fmt.Println(a, b) }
以上示例輸出結果爲:
yisonli Golang
4 循環
for循環是一個循環控制結構,能夠執行指定次數的循環。Go語言的For循環有3種形式,具體定義以下:
// 1. 相似 C 語言的 for for init; condition; post { } // 2. 相似 C 的 while for condition { } // 3. 相似 C 的 for(;;) for { }
- init: 通常爲賦值表達式,給控制變量賦初值;
- condition: 關係表達式或邏輯表達式,循環控制條件;
- post: 通常爲賦值表達式,給控制變量增量或減量。
【示例】
package main import "fmt" func main() { var b int = 7 var a int numbers := [6]int{1, 2, 3, 5} /* for 循環 */ for a := 0; a < 5; a++ { fmt.Printf("a 的值爲: %d\n", a) } for a < b { a++ fmt.Printf("a 的值爲: %d\n", a) } for i,x:= range numbers { fmt.Printf("第 %d 位 x 的值 = %d\n", i,x) } }
以上示例輸出結果爲:
a 的值爲: 0 a 的值爲: 1 a 的值爲: 2 a 的值爲: 3 a 的值爲: 4 a 的值爲: 1 a 的值爲: 2 a 的值爲: 3 a 的值爲: 4 a 的值爲: 5 a 的值爲: 6 a 的值爲: 7 第 0 位 x 的值 = 1 第 1 位 x 的值 = 2 第 2 位 x 的值 = 3 第 3 位 x 的值 = 5 第 4 位 x 的值 = 0 第 5 位 x 的值 = 0
5 條件語句
語句 描述 if 語句 if 語句 由一個布爾表達式後緊跟一個或多個語句組成。 if...else 語句 if 語句 後可使用可選的 else 語句,
else 語句中的表達式在布爾表達式爲 false 時執行。if 嵌套語句 你能夠在 if 或 else if 語句中嵌入一個或多個 if 或 else if 語句。 switch 語句 switch 語句用於基於不一樣條件執行不一樣動做。 select 語句 select 語句相似於 switch 語句,可是select會隨機執行一個可運行的case。
若是沒有case可運行,它將阻塞,直到有case可運行。注意:Go 沒有三目運算符,因此不支持 ?: 形式的條件判斷。
【示例】
package main import "fmt" func main() { /* 局部變量定義 */ var a int = 100; /* 判斷布爾表達式 */ if a < 20 { /* 若是條件爲 true 則執行如下語句 */ fmt.Printf("a 小於 20\n" ); } else { /* 若是條件爲 false 則執行如下語句 */ fmt.Printf("a 不小於 20\n" ); } fmt.Printf("a 的值爲 : %d\n", a); }
以上示例輸出結果爲:
a 不小於 20 a 的值爲 : 100
6 Package包
【包的定義和特性】:
- 包是結構化代碼的一種方式
- 每一個程序都由包(一般簡稱爲 pkg)的概念組成
- 每一個 Go 文件都屬於且僅屬於一個包
- 一個包能夠由許多以
.go
爲擴展名的源文件組成- 你必須在源文件中非註釋的第一行指明這個文件屬於哪一個包,如:
packagemain
- 每一個 Go 應用程序都包含一個名爲
main
的包- 經過
import
關鍵字能夠將一組包連接在一塊兒,在你的應用中導入後方可以使用【注意事項】:
- 若是你導入了一個包卻沒有使用它,則會在構建程序時引起錯誤,如
importedandnotused:os
,這正是遵循了 Go 的格言:「沒有沒必要要的代碼!「。- 可見性規則
- 當標識符(包括常量、變量、類型、函數名、結構字段等等)以一個大寫字母開頭,如:Group1,那麼使用這種形式的標識符的對象就能夠被外部包的 代碼所使用(客戶端程序須要先導入這個包),這被稱爲導出(像面嚮對象語言中的
public
);- 標識符若是以小寫字母開頭,則對包外是不可見的,可是他們在整個包的內部是可見而且可用的(像面嚮對象語言中的
private
)。7 類型轉換
【普通類型轉換】
type_name(expression)
其中type_name 爲類型,expression 爲表達式。
【格式化輸出】
格式化在邏輯中很是經常使用。使用格式化函數,要注意寫法:
fmt.Sprintf(格式化樣式, 參數列表…)
- 格式化樣式:字符串形式,格式化動詞以%開頭。
- 參數列表:多個參數以逗號分隔,個數必須與格式化樣式中的個數一一對應,不然運行時會報錯。
【json轉換】
這裏說的json實際上是json格式的string字符串類型,一般json須要轉換成struct結構體、或者轉換成map映射,正向轉換和反向轉換其實會常常用到。
在此,咱們須要藉助Go的標準庫"encoding/json"
來幫咱們完成轉換的操做了。// struct或map 轉成 json字符串 str, err := json.Marshal(object) // json字符串 轉成 struct或map err := json.Unmarshal([]byte(str), &object)
擴展:網上有些Go的開發小夥伴們在抱怨說標準庫的json效率比較低,並且有幾個不錯的開源json庫能夠提高2-3倍的轉換性能。該如何取捨就全憑我的喜愛了,有興趣的小夥伴們能夠自行去深刻了解,本文就再也不展開了。
【示例】
package main import ( "encoding/json" "fmt" ) func main () { // 1. 普通類型轉換 var sum int = 17 var count int = 5 var mean float32 mean = float32(sum)/float32(count) fmt.Printf("mean 的值爲: %f\n",mean) fmt.Println() // 2. 格式化 title := fmt.Sprintf("已採集%d個藥草, 還須要%d個完成任務。", sum, count) fmt.Println(title) fmt.Println() // 3. json字符串 轉成 struct或map var jsonBlob = [] byte (`[ { "Name" : "Platypus" , "Order" : "Monotremata" } , { "Name" : "Quoll" , "Order" : "Dasyuromorphia" } ]`) type Animal struct { Name string Order string } var animals [] Animal err := json.Unmarshal(jsonBlob, &animals) if err != nil { fmt.Println("error:", err) } fmt.Printf("%+v\n", animals) var animalsMap []map[string]interface{} err1 := json.Unmarshal(jsonBlob, &animalsMap) if err1 != nil { fmt.Println("error:", err1) } fmt.Printf("%+v\n", animalsMap) fmt.Println() // 4. struct或map 轉成 json字符串 type ColorGroup struct { ID int Name string Colors [] string } group := ColorGroup { ID : 1 , Name : "Reds" , Colors : [] string {"Crimson", "Red", "Ruby", "Maroon"} , } groupStr , err2 := json.Marshal(group) if err2 != nil { fmt.Println("error:", err2) } fmt.Printf("%s\n", groupStr) var groupMap = map[string]interface{} {"ID":1,"Name":"Reds","Colors":[] string {"Crimson", "Red", "Ruby", "Maroon"}} groupStr1 , err3 := json.Marshal(groupMap) if err3 != nil { fmt.Println("error:", err3) } fmt.Printf("%s\n", groupStr1) }
以上示例輸出結果爲:
mean 的值爲: 3.400000 已採集17個藥草, 還須要5個完成任務。 [{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}] [map[Name:Platypus Order:Monotremata] map[Name:Quoll Order:Dasyuromorphia]] {"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]} {"Colors":["Crimson","Red","Ruby","Maroon"],"ID":1,"Name":"Reds"}
5、項目開發 - 必備知識點
根據項目難易程度,須要掌握的技術知識點會有所不一樣;本文會根據項目中經常使用的、基礎的、必須掌握的功能點,逐個進行講解和示例,但願我這些小小的經驗和用心的分享,能真的幫助到你,讓你在Go的世界裏翱翔。
1 BeeGo的執行過程
- main 函數是入口函數
- main 引入了一個路由包:
_"firstPro/routers"
- 路由包執行了路由註冊,定位了對應的 Controller:
beego.Router("/",&controllers.MainController{})
- Controller 負責完成具體的業務邏輯
2 路由配置
BeeGo支持的路由配置方式有不少,但正由於多因此短期理解起來會比較難,因此這裏只列出了大體的分類,以及最爲基本的配置方式。先把簡單的理解了,若是我的確實有額外特殊的需求,再另行深刻,也會清晰得多。
【基礎路由】
普通的Get和Post,參見以下代碼:beego.Get("/",func(ctx *context.Context){ ctx.Output.Body([]byte("this is get")) }) beego.Post("/save",func(ctx *context.Context){ ctx.Output.Body([]byte("this is post")) })
【RESTful路由】
// 全匹配, 自動找Controller內對應REST的方法 beego.Router("/", &controllers.MainController{}) // 自定義規則, 第三個參數就是用來設置對應 method 到函數名 beego.Router("/api/list",&RestController{},"*:List") beego.Router("/api/create",&RestController{},"post:Create")
【註解路由】
用戶無需在 router 中註冊路由,只須要 Include 相應地 controller,而後在 controller 的 method 方法上面寫上 router 註釋(// @router
)就能夠了。func init() { ns := beego.NewNamespace("/v1", beego.NSNamespace("/customer", beego.NSInclude( &controllers.CustomerController{}, &controllers.CustomerCookieCheckerController{}, ), ), beego.NSNamespace("/cms", beego.NSInclude( &controllers.CMSController{}, ), ), ) beego.AddNamespace(ns) }
注意:爲了生成swagger自動化文檔,只支持
Namespace+Include
寫法的解析,其餘寫法函數不會自動解析,並且只支持二級解析(一級版本號,二級分別表示應用模塊)3 請求數據處理
【參數獲取】
咱們常常須要獲取用戶傳遞的數據,包括 Get、POST 等方式的請求,beego 裏面會自動解析這些數據,你能夠經過以下方式獲取數據:GetString(key string) string GetStrings(key string) []string GetInt(key string) (int64, error) GetBool(key string) (bool, error) GetFloat(key string) (float64, error)
【解析到Struct】
適用於Form表單提交的形式,使用方法也很簡單,先定義個結構體,而後調用this.ParseForm(結構體指針)
便可。注意:
- 定義 struct 時,字段名後若是有 form 這個 tag,則會以把 form 表單裏的 name 和 tag 的名稱同樣的字段賦值給這個字段,不然就會把 form 表單裏與字段名同樣的表單內容賦值給這個字段。
- 調用 ParseForm 這個方法的時候,傳入的參數必須爲一個 struct 的指針,不然對 struct 的賦值不會成功並返回 xx must be a struct pointer 的錯誤。
- 若是要忽略一個字段,有兩種辦法,一是:字段名小寫開頭,二是:form 標籤的值設置爲 -
【原始請求數據】
- 在配置文件
app.conf
裏設置copyrequestbody=true
- 在 Controller 中使用
this.Ctx.Input.RequestBody
獲取更多其餘的 request 的信息,用戶能夠經過
this.Ctx.Request
獲取【json參數返回】
在 Controller 中給this.Data["json"]
賦值, 而後調用this.ServeJSON()
便可【示例】
package controllers import ( "github.com/astaxie/beego" "fmt" ) type MainController struct { beego.Controller } type user struct { Id int `form:"-"` Name interface{} `form:"username"` Age int `form:"age"` Email string } func (this *MainController) Post() { email := this.GetString("Email") fmt.Println(email) body := this.Ctx.Input.RequestBody fmt.Printf("%s\n", string(body)) u := user{} if err := this.ParseForm(&u); err != nil { //handle error this.Data["json"] = map[string]interface{}{"code":-1, "message":"ParseForm fail", "result":err} } else { fmt.Printf("%+v\n", u) this.Data["json"] = map[string]interface{}{"code":0, "message":"ok"} } this.ServeJSON() this.StopRun() }
以上示例控制檯結果爲:
// 模擬請求:curl -X POST -d "username=yisonli&age=18&Email=yisonli@vip.qq.com" "http://127.0.0.1:8080" yisonli@vip.qq.com username=yisonli&age=18&Email=yisonli@vip.qq.com {Id:0 Name:yisonli Age:18 Email:yisonli@vip.qq.com}
4 數據庫
使用數據庫前需先裝好數據庫驅動,其實只要執行
goget-u
指令便可。
已支持數據庫驅動:
- MySQL:github.com/go-sql-driver/mysql
- PostgreSQL:github.com/lib/pq
- Sqlite3:github.com/mattn/go-sqlite3
【鏈接數據庫】
- 將你須要使用的 driver 加入 import 中
import ( _ "github.com/go-sql-driver/mysql" _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" )
按我的項目需求來選擇便可,本文將以mysql爲例進行演示和說明
- 以本身實際的數據庫配置,初始化並鏈接數據庫
orm.RegisterDriver("mysql", orm.DRMySQL) orm.RegisterDataBase("default", "mysql", "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8&loc=Local")
注:
- loc=Local 是將操做數據庫的時區,設置成跟本地時區同樣。
- 若是你想像我同樣,在本機運行mysql的服務端,而你又剛好裝了Docker,那麼恭喜你,只須要一條指令啓動docker鏡像便可:
docker run--name mysqlserver-e MYSQL_ROOT_PASSWORD=123456-d-i-p3306:3306mysql:5.7
【原生CRUD】
package controllers import ( "github.com/astaxie/beego" "github.com/astaxie/beego/orm" _ "github.com/go-sql-driver/mysql" "fmt" ) type TestController struct { beego.Controller } func (this *TestController) Get() { orm.RegisterDriver("mysql", orm.DRMySQL) orm.RegisterDataBase("default", "mysql", "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8&loc=Local") orm.Debug = true //是否開啓sql調試,開啓時能夠打印全部執行的sql語句,不設置時默認爲false o := orm.NewOrm() o.Using("default") //若是鏈接了多個數據庫,此方法能夠用來切換,不設置時默認是default的DataBase var maps []orm.Params num1, err1 := o.Raw("SELECT * FROM users").Values(&maps) if num1 > 0 && err1 == nil { for _,term := range maps{ fmt.Printf("%+v\n",term) } } res2, err2 := o.Raw("INSERT INTO `users` (`name`, `age`, `email`) VALUES ('Test', 27, 'test@gmail.com')").Exec(); if err2 == nil { num2, _ := res2.RowsAffected() fmt.Println("mysql row affected nums: ", num2) } res3, err3 := o.Raw("UPDATE `users` SET `age`=18 WHERE `name`='Test'").Exec() if err3 == nil { num3, _ := res3.RowsAffected() fmt.Println("mysql row affected nums: ", num3) } res4, err4 := o.Raw("DELETE FROM `users` WHERE `name`='Test'").Exec() if err4 == nil { num4, _ := res4.RowsAffected() fmt.Println("mysql row affected nums: ", num4) } this.Data["json"] = map[string]interface{}{"code":0, "message":"ok"} this.ServeJSON() }
運行結果:
[ORM]2019/07/17 15:30:51 -[Queries/default] - [ OK / db.Query / 0.9ms] - [SELECT * FROM users] map[email:Lily@qq.com id:1 name:Lily age:18] map[id:2 name:Lucy age:20 email:Lucy@gmail.com] map[email:Honey@foxmail.com id:3 name:Honey age:30] [ORM]2019/07/17 15:30:51 -[Queries/default] - [ OK / db.Exec / 6.9ms] - [INSERT INTO `users` (`name`, `age`, `email`) VALUES ('Test', 27, 'test@gmail.com')] mysql row affected nums: 1 [ORM]2019/07/17 15:30:51 -[Queries/default] - [ OK / db.Exec / 3.1ms] - [UPDATE `users` SET `age`=18 WHERE `name`='Test'] mysql row affected nums: 1 [ORM]2019/07/17 15:30:51 -[Queries/default] - [ OK / db.Exec / 6.4ms] - [DELETE FROM `users` WHERE `name`='Test'] mysql row affected nums: 1 2019/07/17 15:30:51.054 [D] [server.go:2741] | 127.0.0.1| 200 | 28.352094ms| match| GET /test/ r:/test
【ORM】
ORM是一個比較強大的功能,他可讓咱們的表結構,經過Struct定義的方式表現&關聯起來,方便使用。
目前該框架仍處於開發階段,讓咱們來看看官網的示例吧:models.go
package main import ( "github.com/astaxie/beego/orm" ) type User struct { Id int Name string Profile *Profile `orm:"rel(one)"` // OneToOne relation Post []*Post `orm:"reverse(many)"` // 設置一對多的反向關係 } type Profile struct { Id int Age int16 User *User `orm:"reverse(one)"` // 設置一對一反向關係(可選) } type Post struct { Id int Title string User *User `orm:"rel(fk)"` //設置一對多關係 Tags []*Tag `orm:"rel(m2m)"` } type Tag struct { Id int Name string Posts []*Post `orm:"reverse(many)"` } func init() { // 須要在init中註冊定義的model orm.RegisterModel(new(User), new(Post), new(Profile), new(Tag)) }
main.go
package main import ( "fmt" "github.com/astaxie/beego/orm" _ "github.com/go-sql-driver/mysql" ) func init() { orm.RegisterDriver("mysql", orm.DRMySQL) orm.RegisterDataBase("default", "mysql", "root:root@/orm_test?charset=utf8") } func main() { o := orm.NewOrm() o.Using("default") // 默認使用 default,你能夠指定爲其餘數據庫 profile := new(Profile) profile.Age = 30 user := new(User) user.Profile = profile user.Name = "slene" fmt.Println(o.Insert(profile)) fmt.Println(o.Insert(user)) }
BeeGo還封裝了不少高級的查詢方法,有興趣的小夥伴能夠額外深刻了解一下;由於篇幅有限,這裏就再也不展開了。
5 Http請求
httplib 庫主要用來模擬客戶端發送 HTTP 請求,相似於 Curl 工具,支持 JQuery 相似的鏈式操做。
// 首先導入包 import ( "github.com/astaxie/beego/httplib" ) // 而後初始化請求方法,返回對象 req := httplib.Get("http://yyeer.com/") // 超時時間、Header頭均可以按需設置 req.SetTimeout(100 * time.Second, 30 * time.Second) req.Header("Host","yyeer.com") // 而後咱們就能夠獲取數據了 str, err := req.String() if err != nil { fmt.Println(err) } fmt.Println(str)
【支持的方法對象】
- Get(url string)
- Post(url string)
- Put(url string)
- Delete(url string)
- Head(url string)
【獲取返回結果】
- 返回 Response 對象,
req.Response()
方法- 這個是 http.Response 對象,用戶能夠本身讀取 body 的數據等。
- 返回 bytes,
req.Bytes()
方法- 返回 string,
req.String()
方法- 保存爲文件,
req.ToFile(filename)
方法- 解析爲 JSON 結構,
req.ToJSON(&result)
方法- 解析爲 XML 結構,
req.ToXml(&result)
方法6 Swagger文檔
BeeGo框架內自動集成了swagger模塊,要使得文檔工做,你須要作幾個事情,
- 第一開啓應用內文檔開關,在配置文件中設置:
EnableDocs=true
,- 而後在你的 main.go 函數中引入
_"beeapi/docs"
(beego 1.7.0 以後版本不須要添加該引用)。- 這樣你就已經內置了 docs 在你的 API 應用中,而後你就使用
bee run-gendoc=true-downdoc=true
, 讓咱們的 API 應用跑起來
- -gendoc=true 表示每次自動化的 build 文檔,
- -downdoc=true 就會自動的下載 swagger 文檔查看器
- 最後,router 和 controller 內的配置和註釋按照規範編寫,便可
【全局註釋】
必須設置在 routers/router.go 中,文件的註釋,最頂部:// @APIVersion 1.0.0 // @Title mobile API // @Description mobile has every tool to get any job done, so codename for the new mobile APIs. // @Contact astaxie@gmail.com package routers
全局的註釋如上所示,是顯示給全局應用的設置信息,有以下這些設置
- @APIVersion
- @Title
- @Description
- @Contact
- @TermsOfServiceUrl
- @License
- @LicenseUrl
【應用註釋】
// CMS API type CMSController struct { beego.Controller } // @Title getStaticBlock // @Description get all the staticblock by key // @Param key path string true "The email for login" // @Success 200 {object} models.ZDTCustomer.Customer // @Failure 400 Invalid email supplied // @Failure 404 User not found // @router /staticblock/:key [get] func (c *CMSController) StaticBlock() { }
首先是 CMSController 定義上面的註釋,這個是用來顯示這個模塊的做用。
接下來就是每個函數上面的註釋,這裏列出來支持的各類註釋:
- @Title
- 這個 API 所表達的含義,是一個文本,空格以後的內容所有解析爲 title
- @Description
- 這個 API 詳細的描述,是一個文本,空格以後的內容所有解析爲 Description
- @Param
- 參數,表示須要傳遞到服務器端的參數,有五列參數,使用空格或者 tab 分割,五個分別表示的含義以下
- 參數名
- 參數類型,能夠有的值是
formData
、query
、path
、body
、header
,formData 表示是 post 請求的數據,query 表示帶在 url 以後的參數,path 表示請求路徑上得參數,例如上面例子裏面的 key,body 表示是一個 raw 數據請求,header 表示帶在 header 信息中得參數。- 參數類型
- 是否必須
- 註釋
- @Success
- 成功返回給客戶端的信息,三個參數,三個參數必須經過空格分隔
- 第一個是 status code。
- 第二個參數是返回的類型,必須使用 {} 包含,
- 第三個是返回的對象或者字符串信息,若是是 {object} 類型,那麼 bee 工具在生成 docs 的時候會掃描對應的對象。
- @Failure
- 失敗返回的信息,包含兩個參數,使用空格分隔
- 第一個表示 status code,
- 第二個表示錯誤信息
- @router
- 路由信息,包含兩個參數,使用空格分隔,
- 第一個是請求的路由地址,支持正則和自定義路由,和以前的路由規則同樣,
- 第二個參數是支持的請求方法,放在 [] 之中,若是有多個方法,那麼使用 , 分隔。
7 日誌
【常規使用】
首先引入包:import ( "github.com/astaxie/beego/logs" )
而後添加輸出引擎:
- 第一個參數是引擎名:
logs.SetLogger("console")
- 第二個參數,用來表示配置信息:
logs.SetLogger(logs.AdapterFile,{"filename":"project.log","level":7,"maxlines":0,"maxsize":0,"daily":true,"maxdays":10,"color":true}`)
log 支持同時輸出到多個引擎,包括:console、file、conn、smtp、es、multifile
使用方式:
beego.Emergency("this is emergency")
beego.Alert("this is alert")
beego.Critical("this is critical")
beego.Error("this is error")
beego.Warning("this is warning")
beego.Notice("this is notice")
beego.Informational("this is informational")
beego.Debug("this is debug")
【自定義格式】
若是框架自帶的日誌功能還沒法知足你的需求,那可能就得麻煩一點,自定義日誌格式。操做流程大體爲:
- 打開要寫入的文件,不存在則建立,
os.OpenFile
- 建立一個自定義的日誌對象
log.New
- 使用時按所需格式輸出日誌
Printf
File, err := os.OpenFile(logdir, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) if nil != err { log.Fatal(err) } MyLog = log.New(io.MultiWriter(File,os.Stderr), "", 0) MyLog.Printf("本身定義的格式... %s\n", 本身定義的參數, fmt.Sprintf(formating, args...))
實際使用時, 能夠把
MyLog.Printf
封裝成一個全局方法,這樣整個項目裏均可使用。8 加解密
加解密在多方對接的時候,常常會用到,因此稍微瞭解一點也是必要的。
由於這塊使用的人比較多,因此只要肯稍微花點時間,在網上都是能夠找到答案的。【MD5】
MD5主要是用做簽名,具體生成方法以下:import ( "crypto/md5" "encoding/hex" ) func MyMd5(Str string, Key string) string { md5ctx := md5.New() md5ctx.Write([]byte(Str + Key)) return hex.EncodeToString(md5ctx.Sum(nil)) }
【AES】
AES主要是用做加解密,還分好幾種不一樣的模式如:ECB、CBC、CFB等。
網上找到一份看起來還不錯的《AES的加解密實現》,原諒個人小偷懶(尚未在實際項目驗證過),God Bless。其餘還有SHA、DES、RSA等加解密方式,具體就得根據各小夥伴不一樣項目所需了。
Go Go Go !去吧,你能夠的!9 緩存使用
// 首先引入包: import ( "github.com/astaxie/beego/cache" ) // 而後初始化一個全局變量對象: bm, err := cache.NewCache("memory", `{"interval":60}`) // 而後咱們就可使用bm增刪改緩存: bm.Put("astaxie", 1, 10*time.Second) bm.Get("astaxie") bm.IsExist("astaxie") bm.Delete("astaxie")
【配置說明】
- memory
- 配置信息以下所示,配置的信息表示 GC 的時間,表示每一個 60s 會進行一次過時清理:
{"interval":60}
- file
- 配置信息以下所示,配置 CachePath 表示緩存的文件目錄,FileSuffix 表示文件後綴,DirectoryLevel 表示目錄層級,EmbedExpiry 表示過時設置
{"CachePath":"./cache","FileSuffix":".cache","DirectoryLevel":"2","EmbedExpiry":"120"}
- redis
- 配置信息以下所示,redis 採用了庫 redigo:
{"key":"collectionName","conn":":6039","dbNum":"0","password":"thePassWord"}
key
: Redis collection 的名稱conn
: Redis 鏈接信息dbNum
: 鏈接 Redis 時的 DB 編號. 默認是0.password
: 用於鏈接有密碼的 Redis 服務器.- memcache
- 配置信息以下所示,memcache 採用了 vitess的庫,表示 memcache 的鏈接地址:
{"conn":"127.0.0.1:11211"}
【示例】
import ( "fmt" "github.com/astaxie/beego" "github.com/astaxie/beego/cache" _ "github.com/astaxie/beego/cache/redis" "time" ) func (this *TestController) Test() { bm, err := cache.NewCache("redis", `{"key":"127.0.0.1","conn":":6379","dbNum":"0","password":""}`) if err != nil { fmt.Println(err) } fmt.Printf("bm = %+v\n",bm) err1 := bm.Put("yisonli", 1, 10*time.Second) if err1 != nil { fmt.Println(err1) } redisValue := bm.Get("yisonli") fmt.Printf("redisValue = %s\n",redisValue) this.Ctx.Output.Body([]byte("OK")) }
執行效果:
bm = &{p:0xc000210240 conninfo::6379 dbNum:0 key:127.0.0.1 password: maxIdle:3} redisValue = 1
注:
- 示例中使用了redis做爲緩存,若是不依賴beego的cache模塊,redis還有不少很好用的數據類型和功能方法,如:Hash散列、List列表、Set集合、SortedSet有序集合。
- 本機啓動redis服務,和上文啓動mysql相似,只須要一條指令啓動docker鏡像便可:
docker run--namelocal-redis-p6379:6379-v $PWD/data:/data-d redis redis-server--appendonly yes
10 Session
beego 內置了 session 模塊,使用 session 至關方便。
方式一、 在 main 入口函數中設置:
beego.BConfig.WebConfig.Session.SessionOn=true
方式二、 經過配置文件配置:
sessionon=true
session默認採用 memory 的方式進行存儲,若是須要更換別的引擎(以redis爲例),須要修改&設置以下配置:
beego.BConfig.WebConfig.Session.SessionProvider = "redis" beego.BConfig.WebConfig.Session.SessionProviderConfig = "127.0.0.1:6379"
【示例】
func (this *MainController) Get() { v := this.GetSession("asta") if v == nil { this.SetSession("asta", int(1)) this.Data["num"] = 0 } else { this.SetSession("asta", v.(int)+1) this.Data["num"] = v.(int) } this.Data["Website"] = "beego.me" this.Data["Email"] = "astaxie@gmail.com" fmt.Printf("%+v\n", this.Data) this.TplName = "index.tpl" }
運行後,屢次訪問首頁的debug輸出結果:
2019/07/17 20:25:20.235 [I] [asm_amd64.s:1333] http server Running on http://:8080 map[RouterPattern:/ num:0 Website:beego.me Email:astaxie@gmail.com] 2019/07/17 20:25:25.141 [D] [server.go:2741] | 127.0.0.1| 200 | 4.63737ms| match| GET / r:/ map[num:1 Website:beego.me Email:astaxie@gmail.com RouterPattern:/] 2019/07/17 20:25:46.021 [D] [server.go:2741] | 127.0.0.1| 200 | 5.116566ms| match| GET / r:/ map[RouterPattern:/ num:2 Website:beego.me Email:astaxie@gmail.com] 2019/07/17 20:26:00.084 [D] [server.go:2741] | 127.0.0.1| 200 | 1.573909ms| match| GET / r:/ map[RouterPattern:/ num:3 Website:beego.me Email:astaxie@gmail.com] 2019/07/17 20:26:12.470 [D] [server.go:2741] | 127.0.0.1| 200 | 2.652028ms| match| GET / r:/
掌握了以上那麼多技能,如今,你對項目的開發有多少把握了呢?
其實,基本已經差很少了,剩下的就是動手實踐了。古人學問無遺力,少壯工夫老始成。
紙上得來終覺淺,絕知此事要躬行。我是Yison,若是我有幫助到你,也請你幫助一下我,隨手一個贊對你來講無足輕重,但倒是使我不斷前行的動力!