以项目为钥匙开启Go的世界

本文不一样于其余Go语言学习的文章,将以项目开发所需为基础,带你飞速踏入Go的世界,成为能独挡一面的强者。当你稍微花几分钟的时间,看完本文的时候,或许你会发现,驾驭Go语言为己所用是如此简单。
山不在高,有仙则名,水不在深,有龙则灵,文不在多,一篇足以。但愿我这些小小的经验和用心的分享,能真的帮助到您。css

1、Go 语言简介

1 Go 语言介绍

Go 即Golang,是Google公司2009年11月正式对外公开的一门编程语言。
Go是静态强类型语言,是区别于解析型语言的编译型语言。mysql

解析型语言——源代码是先翻译为中间代码,而后由解析器对代码进行解释执行。
编译型语言——源代码编译生成机器语言,而后由机器直接执行机器码便可执行。linux

2 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)

添加环境变量方法:

  1. 命令行直接export(对当前终端有效)
  2. 将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

常见问题:

  1. git 没有安装,请自行安装不一样平台的 git,如何安装请看《Git官方安装说明》
  2. 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 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。

【声明变量】

  1. 使用 var 关键字:
    varname type
  2. 一次声明多个变量:
    varname1,name2 type
  3. 省略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 的值为: 70 位 x 的值 = 11 位 x 的值 = 22 位 x 的值 = 33 位 x 的值 = 54 位 x 的值 = 05 位 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 关键字能够将一组包连接在一块儿,在你的应用中导入后方可以使用

【注意事项】

  1. 若是你导入了一个包却没有使用它,则会在构建程序时引起错误,如 importedandnotused:os,这正是遵循了 Go 的格言:“没有没必要要的代码!“。
  2. 可见性规则
  • 当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就能够被外部包的 代码所使用(客户端程序须要先导入这个包),这被称为导出(像面向对象语言中的 public);
  • 标识符若是以小写字母开头,则对包外是不可见的,可是他们在整个包的内部是可见而且可用的(像面向对象语言中的 private )。

7 类型转换

【普通类型转换】

  1. type_name(expression)

 

其中type_name 为类型,expression 为表达式。

【格式化输出】
格式化在逻辑中很是经常使用。使用格式化函数,要注意写法:

  1. 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 标签的值设置为 -

【原始请求数据】

  1. 在配置文件 app.conf 里设置 copyrequestbody=true
  2. 在 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 指令便可。
已支持数据库驱动:

【链接数据库】

  1. 将你须要使用的 driver 加入 import 中
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
_ "github.com/mattn/go-sqlite3"
)

 

按我的项目需求来选择便可,本文将以mysql为例进行演示和说明

  1. 以本身实际的数据库配置,初始化并链接数据库
orm.RegisterDriver("mysql", orm.DRMySQL)
orm.RegisterDataBase("default", "mysql", "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8&loc=Local")

 

注:

  1. loc=Local 是将操做数据库的时区,设置成跟本地时区同样。
  2. 若是你想像我同样,在本机运行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模块,要使得文档工做,你须要作几个事情,

  1. 第一开启应用内文档开关,在配置文件中设置: EnableDocs=true,
  2. 而后在你的 main.go 函数中引入 _"beeapi/docs"(beego 1.7.0 以后版本不须要添加该引用)。
  3. 这样你就已经内置了 docs 在你的 API 应用中,而后你就使用 bee run-gendoc=true-downdoc=true, 让咱们的 API 应用跑起来
  • -gendoc=true 表示每次自动化的 build 文档,
  • -downdoc=true 就会自动的下载 swagger 文档查看器
  1. 最后,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")
  • 第二个参数,用来表示配置信息:
  1. 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

使用方式:

  1. beego.Emergency("this is emergency")
  2. beego.Alert("this is alert")
  3. beego.Critical("this is critical")
  4. beego.Error("this is error")
  5. beego.Warning("this is warning")
  6. beego.Notice("this is notice")
  7. beego.Informational("this is informational")
  8. beego.Debug("this is debug")

【自定义格式】
若是框架自带的日志功能还没法知足你的需求,那可能就得麻烦一点,自定义日志格式。

操做流程大体为:

  1. 打开要写入的文件,不存在则建立, os.OpenFile
  2. 建立一个自定义的日志对象 log.New
  3. 使用时按所需格式输出日志 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

 

注:

  1. 示例中使用了redis做为缓存,若是不依赖beego的cache模块,redis还有不少很好用的数据类型和功能方法,如:Hash散列、List列表、Set集合、SortedSet有序集合。
  2. 本机启动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,若是我有帮助到你,也请你帮助一下我,随手一个赞对你来讲无足轻重,但倒是使我不断前行的动力!

相关文章
相关标签/搜索