本文不一样于其余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,若是我有帮助到你,也请你帮助一下我,随手一个赞对你来讲无足轻重,但倒是使我不断前行的动力!