本文不同于其他Go語言學(xué)習(xí)的文章,將以項(xiàng)目開發(fā)所需為基礎(chǔ)角寸,帶你飛速踏入Go的世界菩混,成為能獨(dú)擋一面的強(qiáng)者。當(dāng)你稍微花幾分鐘的時間扁藕,看完本文的時候沮峡,或許你會發(fā)現(xiàn),駕馭Go語言為己所用是如此簡單纹磺。
山不在高帖烘,有仙則名,水不在深橄杨,有龍則靈秘症,文不在多,一篇足以式矫。希望我這些小小的經(jīng)驗(yàn)和用心的分享乡摹,能真的幫助到您。
一采转、Go 語言簡介
1 Go 語言介紹
Go 即Golang聪廉,是Google公司2009年11月正式對外公開的一門編程語言。
Go是靜態(tài)強(qiáng)類型語言故慈,是區(qū)別于解析型語言的編譯型語言板熊。
解析型語言——源代碼是先翻譯為中間代碼,然后由解析器對代碼進(jìn)行解釋執(zhí)行察绷。
編譯型語言——源代碼編譯生成機(jī)器語言干签,然后由機(jī)器直接執(zhí)行機(jī)器碼即可執(zhí)行。
2 Go語言特性
- 跨平臺的編譯型語言
- 語法接近C語言
- 管道(channel)拆撼,切片(slice)容劳,并發(fā)(routine)
- 有垃圾回收的機(jī)制
- 支持面向?qū)ο蠛兔嫦蜻^程的編程模式
3 Go 語言特色
- 編程模式比較簡單,沒有復(fù)雜的設(shè)計(jì)模式
- 全部源碼編譯到一個文件闸度,編譯速度很快
- 最新版本也有動態(tài)庫形式竭贩,對跨語言調(diào)用的支撐更到位
- 開源框架比較成熟,新崛起的互聯(lián)網(wǎng)公司都在用
- 如滴滴莺禁,uber留量,百度,阿里巴巴,oppo肪获,vivo等
- 微服務(wù)的開發(fā)模式下Go語言是新寵
4 Go 擅長領(lǐng)域
- 服務(wù)開發(fā)寝凌,web的api開發(fā)柒傻,分布式服務(wù)集群的開發(fā)
- 容器docker是go開源的產(chǎn)品孝赫,k8s等這些都是基于go語言的
- 對高并發(fā)、高性能的系統(tǒng)和服務(wù)支撐红符,Go語言對比其他語言有更快的開發(fā)速度青柄,更高的開發(fā)效率
- 獨(dú)有的語言特性和設(shè)計(jì)模式routine,channel预侯,sync包支撐了海量并行的支持致开。
所以能看到這些領(lǐng)域都在使用Go語言:微服務(wù)開發(fā)模式,api開發(fā)萎馅,rpc服務(wù)開發(fā)双戳,游戲服務(wù)開發(fā)等等
二、框架選擇
Go的開發(fā)框架比較多糜芳,比較知名的幾個分別是Gin飒货、BeeGo、Iris峭竣、Echo塘辅、Revel、Buffalo皆撩。對比排名詳見: 《Go語言Web框架對比》
框架的選擇上扣墩,本人主要遵循如下幾點(diǎn)原則,分別是:
- star多
- 易上手
- 性能佳
- 持續(xù)維護(hù)
選擇過程不多說扛吞,本人最終選擇了beego作為本次入手的框架呻惕,本文余下內(nèi)容如無特別說明,均基于此框架滥比。
三亚脆、環(huán)境部署
1 Go 語言環(huán)境安裝
安裝包下載地址為:https://golang.org/dl/
如果打不開可以使用這個地址:https://golang.google.cn/dl/
【UNIX/Linux/Mac OS X, 和 FreeBSD 安裝】
- 下載二進(jìn)制包:go1.4.linux-amd64.tar.gz
- 將下載的二進(jìn)制包解壓至 /usr/local目錄
tar -C /usr/local -xzf go1.12.7.linux-amd64.tar.gz
- 將 /usr/local/go/bin 目錄添加至PATH環(huán)境變量:
export PATH=$PATH:/usr/local/go/bin
注意:MAC 系統(tǒng)下你可以使用 .pkg 結(jié)尾的安裝包直接雙擊來完成安裝,安裝目錄在 /usr/local/go/ 下守呜。
本文余下內(nèi)容如無特別說明型酥,均默認(rèn)Linux環(huán)境
【W(wǎng)indows 系統(tǒng)下安裝】
Windows 下可以使用 .msi 后綴(在下載列表中可以找到該文件,如go1.12.7.windows-amd64.msi
)的安裝包來安裝查乒。
默認(rèn)情況下.msi文件會安裝在 c:\Go
目錄下弥喉。你可以將 c:\Go\bin
目錄添加到 PATH 環(huán)境變量中。
添加后你需要重啟命令窗口才能生效玛迄。
安裝測試:
創(chuàng)建工作目錄 C:\>Go_WorkSpace
由境。
//test.go 文件代碼:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
使用 go 命令執(zhí)行以上代碼輸出結(jié)果如下:
C:\Go_WorkSpace>go run test.go
Hello, World!
2 Go 語言環(huán)境變量
如下環(huán)境變量,都是Go編譯和運(yùn)行時必要的,如果安裝時沒有自動設(shè)置好虏杰,請務(wù)必自己手動添加讥蟆。
- GOROOT: Go 安裝后的根目錄(例如:/usr/local/go)
- GOPATH: Go 的工作空間,就是我們的開發(fā)和依賴包的目錄(例如:~/go)
添加環(huán)境變量方法:
- 命令行直接export(對當(dāng)前終端有效)
- 將export加到
~/.bashrc
文件里(對當(dāng)前用戶有效纺阔,首次添加后記得source ~/.bashrc
一下)
export GOPATH=~/go
export GOROOT=/usr/local/go
3 BeeGo 框架
【安裝】
安裝方式非常簡單瘸彤,只要敲擊如下命令即可(bee工具可以快速創(chuàng)建項(xiàng)目和自動監(jiān)測運(yùn)行,記得要安裝哦):
go get github.com/astaxie/beego
# bee 工具
go get github.com/beego/bee
安裝完之后笛钝,bee 可執(zhí)行文件默認(rèn)存放在
$GOPATH/bin
里面质况,所以您需要把$GOPATH/bin
添加到您的環(huán)境變量PATH中:export PATH=$PATH:$GOPATH/bin
常見問題:
- git 沒有安裝,請自行安裝不同平臺的 git玻靡,如何安裝請看《Git官方安裝說明》结榄。
- git https 無法獲取,請配置本地的 git囤捻,關(guān)閉 https 驗(yàn)證:
git config --global http.sslVerify false
【創(chuàng)建項(xiàng)目】
打開終端臼朗,進(jìn)入 $GOPATH/src
所在的目錄,用bee工具創(chuàng)建一個項(xiàng)目:bee new firstPro
【目錄結(jié)構(gòu)】
這是一個典型的 MVC 架構(gòu)的應(yīng)用蝎土,main.go 是入口文件视哑。目錄結(jié)構(gòu)如下所示:
.
├── 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
【編譯運(yùn)行】
cd $GOPATH/src/firstPro
進(jìn)入我們創(chuàng)建的項(xiàng)目,使用 bee run
來運(yùn)行該項(xiàng)目瘟则,這樣我們的應(yīng)用就在 8080 端口(beego 的默認(rèn)端口)跑起來了黎炉,讓我們打開瀏覽器看看效果吧:
4 命令使用
Go指令
- 下載:
go get
- 編譯:
go build
- 運(yùn)行:
go run
Bee工具
- 新建項(xiàng)目:
bee api
或bee new
分別創(chuàng)建 api應(yīng)用 和 web項(xiàng)目,位于$GOPATH/src
目錄下 - 運(yùn)行:
bee run
該命令必須在$GOPATH/src/appname
下執(zhí)行 - 打包:
bee pack
以上是一些常用的命令醋拧,介紹雖比較簡單慷嗜,但只要大概記住他的作用即可。
沒有把所有指令都列出來丹壕,因?yàn)槲覀儠簳r還用不到庆械,這些已經(jīng)足夠我們擺平項(xiàng)目的開發(fā)了,其他的指令和高級用法菌赖,有興趣的小伙伴們可以動動手自行查閱缭乘。
四、Go 基礎(chǔ)語法
1 變量
Go 語言變量名由字母琉用、數(shù)字堕绩、下劃線組成,其中首個字符不能為數(shù)字邑时。
【聲明變量】
- 使用 var 關(guān)鍵字:
var name type
- 一次聲明多個變量:
var name1, name2 type
- 省略var形式:
name := value
【零值】
指定變量類型奴紧,如果沒有初始化,則變量默認(rèn)為零值晶丘。
- 數(shù)值類型(包括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)
}
以上示例輸出結(jié)果為:
yisonli
1 2
[10 9 8 7]
map[one:1 two:2]
2 數(shù)據(jù)類型
類型 | 描述 |
---|---|
布爾型 | 布爾型的值只可以是常量 true 或者 false 一個簡單的例子: var b bool = true
|
數(shù)字類型 |
有符號整型: int黍氮、int8唐含、int16、int32沫浆、int64 無符號整型: uint捷枯、uint8、uint16专执、uint32淮捆、uint64 浮點(diǎn)型: float32、float64 復(fù)數(shù): complex64他炊、complex128 其他: byte (類似uint8)争剿、uintptr (存指針)已艰、rune (類似int32) |
字符串類型 | 一串固定長度的字符連接起來的字符序列痊末,使用 UTF-8 編碼標(biāo)識 Unicode 文本 |
派生類型 | 包括: (a) 指針類型(Pointer) (b) 數(shù)組類型 (c) 結(jié)構(gòu)化類型(struct) (d) Channel 類型 (e) 函數(shù)類型 (f) 切片類型 (g) 接口類型(interface) (h) Map 類型 |
3 函數(shù)
Go 語言函數(shù)定義格式如下:
func function_name( [parameter list] ) [return_types] {
函數(shù)體
}
函數(shù)定義解析:
- func:函數(shù)聲明關(guān)鍵字
- function_name:函數(shù)名稱
-
parameter list:參數(shù)列表
- 參數(shù)就像一個占位符,當(dāng)函數(shù)被調(diào)用時哩掺,你可以將值傳遞給參數(shù)凿叠,這個值被稱為實(shí)際參數(shù)。
- 參數(shù)列表指定的是參數(shù)類型嚼吞、順序盒件、及參數(shù)個數(shù)。
- 參數(shù)是可選的舱禽,也就是說函數(shù)也可以不包含參數(shù)炒刁。
-
return_types:返回類型
- 函數(shù)返回一列值, return_types 是該列值的數(shù)據(jù)類型。
- 有些功能不需要返回值誊稚,這種情況下 return_types 不是必須的翔始。
- 函數(shù)體:具體的代碼邏輯
【示例】
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)
}
以上示例輸出結(jié)果為:
yisonli Golang
4 循環(huán)
for循環(huán)是一個循環(huán)控制結(jié)構(gòu),可以執(zhí)行指定次數(shù)的循環(huán)里伯。Go語言的For循環(huán)有3種形式城瞎,具體定義如下:
// 1. 類似 C 語言的 for
for init; condition; post { }
// 2. 類似 C 的 while
for condition { }
// 3. 類似 C 的 for(;;)
for { }
- init: 一般為賦值表達(dá)式,給控制變量賦初值疾瓮;
- condition: 關(guān)系表達(dá)式或邏輯表達(dá)式脖镀,循環(huán)控制條件;
- post: 一般為賦值表達(dá)式狼电,給控制變量增量或減量蜒灰。
【示例】
package main
import "fmt"
func main() {
var b int = 7
var a int
numbers := [6]int{1, 2, 3, 5}
/* for 循環(huán) */
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)
}
}
以上示例輸出結(jié)果為:
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 語句 由一個布爾表達(dá)式后緊跟一個或多個語句組成。 |
if...else 語句 | if 語句 后可以使用可選的 else 語句, else 語句中的表達(dá)式在布爾表達(dá)式為 false 時執(zhí)行肩碟。 |
if 嵌套語句 | 你可以在 if 或 else if 語句中嵌入一個或多個 if 或 else if 語句强窖。 |
switch 語句 | switch 語句用于基于不同條件執(zhí)行不同動作。 |
select 語句 | select 語句類似于 switch 語句腾务,但是select會隨機(jī)執(zhí)行一個可運(yùn)行的case毕骡。 如果沒有case可運(yùn)行,它將阻塞,直到有case可運(yùn)行未巫。 |
注意:Go 沒有三目運(yùn)算符窿撬,所以不支持 ?: 形式的條件判斷。
【示例】
package main
import "fmt"
func main() {
/* 局部變量定義 */
var a int = 100;
/* 判斷布爾表達(dá)式 */
if a < 20 {
/* 如果條件為 true 則執(zhí)行以下語句 */
fmt.Printf("a 小于 20\n" );
} else {
/* 如果條件為 false 則執(zhí)行以下語句 */
fmt.Printf("a 不小于 20\n" );
}
fmt.Printf("a 的值為 : %d\n", a);
}
以上示例輸出結(jié)果為:
a 不小于 20
a 的值為 : 100
6 Package包
【包的定義和特性】:
- 包是結(jié)構(gòu)化代碼的一種方式
- 每個程序都由包(通常簡稱為 pkg)的概念組成
- 每個 Go 文件都屬于且僅屬于一個包
- 一個包可以由許多以
.go
為擴(kuò)展名的源文件組成 - 你必須在源文件中非注釋的第一行指明這個文件屬于哪個包叙凡,如:
package main
- 每個 Go 應(yīng)用程序都包含一個名為
main
的包 - 通過
import
關(guān)鍵字可以將一組包鏈接在一起劈伴,在你的應(yīng)用中導(dǎo)入后方可使用
【注意事項(xiàng)】:
- 如果你導(dǎo)入了一個包卻沒有使用它,則會在構(gòu)建程序時引發(fā)錯誤握爷,如
imported and not used: os
跛璧,這正是遵循了 Go 的格言:“沒有不必要的代碼!“新啼。 - 可見性規(guī)則
- 當(dāng)標(biāo)識符(包括常量追城、變量、類型燥撞、函數(shù)名座柱、結(jié)構(gòu)字段等等)以一個大寫字母開頭,如:Group1物舒,那么使用這種形式的標(biāo)識符的對象就可以被外部包的 代碼所使用(客戶端程序需要先導(dǎo)入這個包)色洞,這被稱為導(dǎo)出(像面向?qū)ο笳Z言中的
public
); - 標(biāo)識符如果以小寫字母開頭冠胯,則對包外是不可見的火诸,但是他們在整個包的內(nèi)部是可見并且可用的(像面向?qū)ο笳Z言中的
private
)。
7 類型轉(zhuǎn)換
【普通類型轉(zhuǎn)換】
type_name(expression)
其中type_name 為類型荠察,expression 為表達(dá)式置蜀。
【格式化輸出】
格式化在邏輯中非常常用。使用格式化函數(shù)割粮,要注意寫法:
fmt.Sprintf(格式化樣式, 參數(shù)列表…)
- 格式化樣式:字符串形式盾碗,格式化動詞以%開頭。
- 參數(shù)列表:多個參數(shù)以逗號分隔舀瓢,個數(shù)必須與格式化樣式中的個數(shù)一一對應(yīng)廷雅,否則運(yùn)行時會報(bào)錯。
【json轉(zhuǎn)換】
這里說的json其實(shí)是json格式的string字符串類型京髓,通常json需要轉(zhuǎn)換成struct結(jié)構(gòu)體航缀、或者轉(zhuǎn)換成map映射,正向轉(zhuǎn)換和反向轉(zhuǎn)換其實(shí)會經(jīng)常用到堰怨。
在此芥玉,我們需要借助Go的標(biāo)準(zhǔn)庫 "encoding/json"
來幫我們完成轉(zhuǎn)換的操作了。
// struct或map 轉(zhuǎn)成 json字符串
str, err := json.Marshal(object)
// json字符串 轉(zhuǎn)成 struct或map
err := json.Unmarshal([]byte(str), &object)
擴(kuò)展:網(wǎng)上有些Go的開發(fā)小伙伴們在抱怨說標(biāo)準(zhǔn)庫的json效率比較低备图,而且有幾個不錯的開源json庫可以提升2-3倍的轉(zhuǎn)換性能灿巧。該如何取舍就全憑個人喜好了赶袄,有興趣的小伙伴們可以自行去深入了解,本文就不再展開了抠藕。
【示例】
package main
import (
"encoding/json"
"fmt"
)
func main () {
// 1. 普通類型轉(zhuǎn)換
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個完成任務(wù)饿肺。", sum, count)
fmt.Println(title)
fmt.Println()
// 3. json字符串 轉(zhuǎn)成 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 轉(zhuǎn)成 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)
}
以上示例輸出結(jié)果為:
mean 的值為: 3.400000
已采集17個藥草, 還需要5個完成任務(wù)。
[{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"}
五盾似、項(xiàng)目開發(fā) - 必備知識點(diǎn)
根據(jù)項(xiàng)目難易程度敬辣,需要掌握的技術(shù)知識點(diǎn)會有所不同;本文會根據(jù)項(xiàng)目中常用的零院、基礎(chǔ)的溉跃、必須掌握的功能點(diǎn),逐個進(jìn)行講解和示例告抄,希望我這些小小的經(jīng)驗(yàn)和用心的分享撰茎,能真的幫助到你,讓你在Go的世界里翱翔玄妈。
1 BeeGo的執(zhí)行過程
- main 函數(shù)是入口函數(shù)
- main 引入了一個路由包:
_ "firstPro/routers"
- 路由包執(zhí)行了路由注冊乾吻,定位了對應(yīng)的 Controller:
beego.Router("/", &controllers.MainController{})
- Controller 負(fù)責(zé)完成具體的業(yè)務(wù)邏輯
2 路由配置
BeeGo支持的路由配置方式有很多,但正因?yàn)槎嗨远虝r間理解起來會比較難拟蜻,所以這里只列出了大致的分類,以及最為基本的配置方式枯饿。先把簡單的理解了酝锅,如果個人確實(shí)有額外特殊的需求,再另行深入奢方,也會清晰得多搔扁。
【基礎(chǔ)路由】
普通的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內(nèi)對應(yīng)REST的方法
beego.Router("/", &controllers.MainController{})
// 自定義規(guī)則, 第三個參數(shù)就是用來設(shè)置對應(yīng) method 到函數(shù)名
beego.Router("/api/list",&RestController{},"*:List")
beego.Router("/api/create",&RestController{},"post:Create")
【注解路由】
用戶無需在 router 中注冊路由蟋字,只需要 Include 相應(yīng)地 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
寫法的解析苛聘,其他寫法函數(shù)不會自動解析,而且只支持二級解析(一級版本號忠聚,二級分別表示應(yīng)用模塊)
3 請求數(shù)據(jù)處理
【參數(shù)獲取】
我們經(jīng)常需要獲取用戶傳遞的數(shù)據(jù)设哗,包括 Get、POST 等方式的請求两蟀,beego 里面會自動解析這些數(shù)據(jù)网梢,你可以通過如下方式獲取數(shù)據(jù):
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表單提交的形式,使用方法也很簡單赂毯,先定義個結(jié)構(gòu)體战虏,然后調(diào)用 this.ParseForm(結(jié)構(gòu)體指針)
即可拣宰。
注意:
- 定義 struct 時,字段名后如果有 form 這個 tag烦感,則會以把 form 表單里的 name 和 tag 的名稱一樣的字段賦值給這個字段徐裸,否則就會把 form 表單里與字段名一樣的表單內(nèi)容賦值給這個字段。
- 調(diào)用 ParseForm 這個方法的時候啸盏,傳入的參數(shù)必須為一個 struct 的指針重贺,否則對 struct 的賦值不會成功并返回 xx must be a struct pointer 的錯誤。
- 如果要忽略一個字段回懦,有兩種辦法气笙,一是:字段名小寫開頭,二是:form 標(biāo)簽的值設(shè)置為 -
【原始請求數(shù)據(jù)】
- 在配置文件
app.conf
里設(shè)置copyrequestbody = true
- 在 Controller 中使用
this.Ctx.Input.RequestBody
獲取
更多其他的 request 的信息怯晕,用戶可以通過
this.Ctx.Request
獲取
【json參數(shù)返回】
在 Controller 中給 this.Data["json"]
賦值潜圃, 然后調(diào)用 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()
}
以上示例控制臺結(jié)果為:
// 模擬請求: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 數(shù)據(jù)庫
使用數(shù)據(jù)庫前需先裝好數(shù)據(jù)庫驅(qū)動,其實(shí)只要執(zhí)行 go get -u
指令即可舟茶。
已支持?jǐn)?shù)據(jù)庫驅(qū)動:
- MySQL:github.com/go-sql-driver/mysql
- PostgreSQL:github.com/lib/pq
- Sqlite3:github.com/mattn/go-sqlite3
【連接數(shù)據(jù)庫】
- 將你需要使用的 driver 加入 import 中
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
_ "github.com/mattn/go-sqlite3"
)
按個人項(xiàng)目需求來選擇即可谭期,本文將以mysql為例進(jìn)行演示和說明
- 以自己實(shí)際的數(shù)據(jù)庫配置,初始化并連接數(shù)據(jù)庫
orm.RegisterDriver("mysql", orm.DRMySQL)
orm.RegisterDataBase("default", "mysql", "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8&loc=Local")
注:
- loc=Local 是將操作數(shù)據(jù)庫的時區(qū)吧凉,設(shè)置成跟本地時區(qū)一樣隧出。
- 如果你想像我一樣,在本機(jī)運(yùn)行mysql的服務(wù)端阀捅,而你又恰好裝了Docker胀瞪,那么恭喜你,只需要一條指令啟動docker鏡像即可:
docker run --name mysqlserver -e MYSQL_ROOT_PASSWORD=123456 -d -i -p 3306:3306 mysql: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調(diào)試饲鄙,開啟時可以打印所有執(zhí)行的sql語句凄诞,不設(shè)置時默認(rèn)為false
o := orm.NewOrm()
o.Using("default") //如果連接了多個數(shù)據(jù)庫,此方法可以用來切換忍级,不設(shè)置時默認(rèn)是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()
}
運(yùn)行結(jié)果:
[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是一個比較強(qiáng)大的功能帆谍,他可以讓我們的表結(jié)構(gòu),通過Struct定義的方式表現(xiàn)&關(guān)聯(lián)起來轴咱,方便使用汛蝙。
目前該框架仍處于開發(fā)階段,讓我們來看看官網(wǎng)的示例吧:
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)"` // 設(shè)置一對多的反向關(guān)系
}
type Profile struct {
Id int
Age int16
User *User `orm:"reverse(one)"` // 設(shè)置一對一反向關(guān)系(可選)
}
type Post struct {
Id int
Title string
User *User `orm:"rel(fk)"` //設(shè)置一對多關(guān)系
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") // 默認(rèn)使用 default嗦玖,你可以指定為其他數(shù)據(jù)庫
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還封裝了很多高級的查詢方法患雇,有興趣的小伙伴可以額外深入了解一下;因?yàn)槠邢抻畲欤@里就不再展開了苛吱。
5 Http請求
httplib 庫主要用來模擬客戶端發(fā)送 HTTP 請求,類似于 Curl 工具器瘪,支持 JQuery 類似的鏈?zhǔn)讲僮鳌?/p>
// 首先導(dǎo)入包
import (
"github.com/astaxie/beego/httplib"
)
// 然后初始化請求方法翠储,返回對象
req := httplib.Get("http://yyeer.com/")
// 超時時間绘雁、Header頭都可以按需設(shè)置
req.SetTimeout(100 * time.Second, 30 * time.Second)
req.Header("Host","yyeer.com")
// 然后我們就可以獲取數(shù)據(jù)了
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)
【獲取返回結(jié)果】
- 返回 Response 對象,
req.Response()
方法- 這個是 http.Response 對象援所,用戶可以自己讀取 body 的數(shù)據(jù)等庐舟。
- 返回 bytes,
req.Bytes()
方法 - 返回 string,
req.String()
方法 - 保存為文件住拭,
req.ToFile(filename)
方法 - 解析為 JSON 結(jié)構(gòu)挪略,
req.ToJSON(&result)
方法 - 解析為 XML 結(jié)構(gòu),
req.ToXml(&result)
方法
6 Swagger文檔
BeeGo框架內(nèi)自動集成了swagger模塊滔岳,要使得文檔工作杠娱,你需要做幾個事情,
- 第一開啟應(yīng)用內(nèi)文檔開關(guān)谱煤,在配置文件中設(shè)置:
EnableDocs = true
, - 然后在你的 main.go 函數(shù)中引入
_ "beeapi/docs"
(beego 1.7.0 之后版本不需要添加該引用)摊求。 - 這樣你就已經(jīng)內(nèi)置了 docs 在你的 API 應(yīng)用中,然后你就使用
bee run -gendoc=true -downdoc=true
, 讓我們的 API 應(yīng)用跑起來
- -gendoc=true 表示每次自動化的 build 文檔刘离,
- -downdoc=true 就會自動的下載 swagger 文檔查看器
- 最后室叉,router 和 controller 內(nèi)的配置和注釋按照規(guī)范編寫,即可
【全局注釋】
必須設(shè)置在 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
全局的注釋如上所示,是顯示給全局應(yīng)用的設(shè)置信息疲憋,有如下這些設(shè)置
- @APIVersion
- @Title
- @Description
- @Contact
- @TermsOfServiceUrl
- @License
- @LicenseUrl
【應(yīng)用注釋】
// 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 定義上面的注釋凿渊,這個是用來顯示這個模塊的作用。
接下來就是每一個函數(shù)上面的注釋缚柳,這里列出來支持的各種注釋:
- @Title
- 這個 API 所表達(dá)的含義,是一個文本搪锣,空格之后的內(nèi)容全部解析為 title
- @Description
- 這個 API 詳細(xì)的描述秋忙,是一個文本,空格之后的內(nèi)容全部解析為 Description
- @Param
- 參數(shù)构舟,表示需要傳遞到服務(wù)器端的參數(shù)灰追,有五列參數(shù),使用空格或者 tab 分割狗超,五個分別表示的含義如下
- 參數(shù)名
- 參數(shù)類型弹澎,可以有的值是
formData
、query
努咐、path
苦蒿、body
、header
渗稍,formData 表示是 post 請求的數(shù)據(jù)佩迟,query 表示帶在 url 之后的參數(shù)团滥,path 表示請求路徑上得參數(shù),例如上面例子里面的 key报强,body 表示是一個 raw 數(shù)據(jù)請求灸姊,header 表示帶在 header 信息中得參數(shù)。 - 參數(shù)類型
- 是否必須
- 注釋
- @Success
- 成功返回給客戶端的信息秉溉,三個參數(shù)力惯,三個參數(shù)必須通過空格分隔
- 第一個是 status code。
- 第二個參數(shù)是返回的類型召嘶,必須使用 {} 包含父晶,
- 第三個是返回的對象或者字符串信息,如果是 {object} 類型苍蔬,那么 bee 工具在生成 docs 的時候會掃描對應(yīng)的對象诱建。
- 成功返回給客戶端的信息秉溉,三個參數(shù)力惯,三個參數(shù)必須通過空格分隔
- @Failure
- 失敗返回的信息,包含兩個參數(shù)碟绑,使用空格分隔
- 第一個表示 status code俺猿,
- 第二個表示錯誤信息
- 失敗返回的信息,包含兩個參數(shù)碟绑,使用空格分隔
- @router
- 路由信息,包含兩個參數(shù)格仲,使用空格分隔押袍,
- 第一個是請求的路由地址,支持正則和自定義路由凯肋,和之前的路由規(guī)則一樣谊惭,
- 第二個參數(shù)是支持的請求方法,放在 [] 之中,如果有多個方法侮东,那么使用 , 分隔圈盔。
- 路由信息,包含兩個參數(shù)格仲,使用空格分隔押袍,
7 日志
【常規(guī)使用】
首先引入包:
import (
"github.com/astaxie/beego/logs"
)
然后添加輸出引擎:
- 第一個參數(shù)是引擎名:
logs.SetLogger("console")
- 第二個參數(shù),用來表示配置信息:
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")
【自定義格式】
如果框架自帶的日志功能還無法滿足你的需求容诬,那可能就得麻煩一點(diǎn)娩梨,自定義日志格式。
操作流程大致為:
- 打開要寫入的文件览徒,不存在則創(chuàng)建狈定,
os.OpenFile
- 創(chuàng)建一個自定義的日志對象
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", 自己定義的參數(shù), fmt.Sprintf(formating, args...))
實(shí)際使用時, 可以把
MyLog.Printf
封裝成一個全局方法,這樣整個項(xiàng)目里都可使用吱殉。
8 加解密
加解密在多方對接的時候掸冤,經(jīng)常會用到厘托,所以稍微了解一點(diǎn)也是必要的。
因?yàn)檫@塊使用的人比較多稿湿,所以只要肯稍微花點(diǎn)時間铅匹,在網(wǎng)上都是可以找到答案的。
【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等罗丰。
網(wǎng)上找到一份看起來還不錯的《AES的加解密實(shí)現(xiàn)》,原諒我的小偷懶(還沒有在實(shí)際項(xiàng)目驗(yàn)證過)再姑,God Bless萌抵。
其他還有SHA、DES元镀、RSA等加解密方式绍填,具體就得根據(jù)各小伙伴不同項(xiàng)目所需了。
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 會進(jìn)行一次過期清理:
{"interval":60}
- file
- 配置信息如下所示,配置 CachePath 表示緩存的文件目錄萝快,F(xiàn)ileSuffix 表示文件后綴锻霎,DirectoryLevel 表示目錄層級,EmbedExpiry 表示過期設(shè)置
{"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 編號. 默認(rèn)是0. -
password
: 用于連接有密碼的 Redis 服務(wù)器.
-
- 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"))
}
執(zhí)行效果:
bm = &{p:0xc000210240 conninfo::6379 dbNum:0 key:127.0.0.1 password: maxIdle:3}
redisValue = 1
注:
- 示例中使用了redis作為緩存氢拥,如果不依賴beego的cache模塊,redis還有很多很好用的數(shù)據(jù)類型和功能方法锨侯,如:Hash散列嫩海、List列表、Set集合囚痴、SortedSet有序集合叁怪。
- 本機(jī)啟動redis服務(wù),和上文啟動mysql類似深滚,只需要一條指令啟動docker鏡像即可:
docker run --name local-redis -p 6379:6379 -v $PWD/data:/data -d redis redis-server --appendonly yes
10 Session
beego 內(nèi)置了 session 模塊奕谭,使用 session 相當(dāng)方便涣觉。
方式1、 在 main 入口函數(shù)中設(shè)置:
beego.BConfig.WebConfig.Session.SessionOn = true
方式2血柳、 通過配置文件配置:
sessionon = true
session默認(rèn)采用 memory 的方式進(jìn)行存儲官册,如果需要更換別的引擎(以redis為例),需要修改&設(shè)置如下配置:
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"
}
運(yùn)行后难捌,多次訪問首頁的debug輸出結(jié)果:
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:/
掌握了以上那么多技能膝宁,現(xiàn)在,你對項(xiàng)目的開發(fā)有多少把握了呢根吁?
其實(shí)员淫,基本已經(jīng)差不多了,剩下的就是動手實(shí)踐了击敌。
古人學(xué)問無遺力介返,少壯工夫老始成。
紙上得來終覺淺沃斤,絕知此事要躬行圣蝎。
我是Yison,如果我有幫助到你轰枝,也請你幫助一下我捅彻,隨手一個贊對你來說無足輕重,但卻是使我不斷前行的動力鞍陨!