您现在的位置是:网站首页> Go语言

Go开发基础技术笔记

摘要

Go开发基础技术笔记

Go-时间日期函数

Go数据库操作

Gin学习笔记

beego学习入门

Beego框架之bee工具

Beego框架之参数配置 

Beego控制器之路由配置

Beego控制器之控制器函数

Beego控制器之获取请求参数

Beego控制器之响应请求

ProtoBuf入门教程

PDF处理库



GO-时间日期函数

一、获取当前时间

package main


import (

"fmt"

"time"

)


func main () {

//获取当前时间

now := time.Now()

fmt.Printf("now=%v now type=%T", now, now)

}


二、获取年月日,时分秒

package main


import (

"fmt"

"time"

)


func main () {

//获取当前时间

now := time.Now()

//获取年月日,时分秒

fmt.Printf("年=%v\n", now.Year())

fmt.Printf("月=%v\n", now.Month())

fmt.Printf("日=%v\n", now.Day())

fmt.Printf("时=%v\n", now.Hour())

fmt.Printf("分=%v\n", now.Minute())

fmt.Printf("秒=%v\n", now.Second())

}


三、格式化日期时间

package main


import (

"fmt"

"time"

)


func main () {

//获取当前时间

now := time.Now()

//格式化日期时间

fmt.Printf("当前年月日 %d-%d-%d %d:%d:%d \n",now.Year(), now.Month(), now.Day(), 

now.Hour(), now.Minute(), now.Second())

// //返回字符串

datestr := fmt.Sprintf("当前年月日 %d-%d-%d %d:%d:%d \n",now.Year(), now.Month(), now.Day(), 

now.Hour(), now.Minute(), now.Second())

fmt.Printf("datestr=%v", datestr)

//Format格式化

fmt.Printf(now.Format("2006/01/02 15:04:05"))//2006/01/02 15:04:05时间不能改

fmt.Println()

fmt.Printf(now.Format("2006/01/02"))

fmt.Println()

fmt.Printf(now.Format("15:04:05"))

fmt.Println()

}


四、时间常量结合sleep使用

package main


import (

"fmt"

"time"

)


func main () {

//每隔1s打印一个数字,打印10退出

//每隔0.1s打印一个数字,10退出

i := 0

for {

i++

fmt.Println(i)

//休眠

//time.Sleep(time.Second)   1s

time.Sleep(time.Millisecond + 100)

if i == 10{

break

}

}


}


五、获取时间戳

package main


import (

"fmt"

"time"

)


func main () {

//获取当前时间

now := time.Now()

//Unix和UnixNano的使用

fmt.Printf("Unix时间戳=%v\nUnixNano时间戳=%v", now.Unix(), now.UnixNano())


}


六、计算时间差

package main


import (

"fmt"

"time"

"strconv"

)


func time1() {

str := ""

for i :=0; i < 100000; i++{

str +="hello" + strconv.Itoa(i)

}

}


func main() {

//在执行前获取时间戳

startime := time.Now().Unix()

time1()

endtime := time.Now().Unix()

fmt.Printf("执行time1()耗费的时间时%v秒\n", endtime - startime)

}

七、字符串转时间

package main


import (

"fmt"

"time"

)


func main()  {


Str2Time:=Str2Time("2017-09-12 12:03:40")

fmt.Println(Str2Time)



Str2Stamp:=Str2Stamp("2017-09-12 12:03:40")

fmt.Println(Str2Stamp)


Time2Str:=Time2Str()

fmt.Println(Time2Str)


GetStamp:=Time2Stamp()

fmt.Println(GetStamp)


Stamp2Str:=Stamp2Str(1505189020000)

fmt.Println(Stamp2Str)


Stamp2Time:=Stamp2Time(1505188820000)

fmt.Println(Stamp2Time)

}


/**字符串->时间对象*/

func Str2Time(formatTimeStr string) time.Time{

timeLayout := "2006-01-02 15:04:05"

loc, _ := time.LoadLocation("Local")

theTime, _ := time.ParseInLocation(timeLayout, formatTimeStr, loc) //使用模板在对应时区转化为time.time类型


return theTime


}

/**字符串->时间戳*/

func Str2Stamp(formatTimeStr string) int64 {

timeStruct:=Str2Time(formatTimeStr)

millisecond:=timeStruct.UnixNano()/1e6

return  millisecond

}


/**时间对象->字符串*/

func Time2Str() string {

const shortForm = "2006-01-01 15:04:05"

t := time.Now()

temp := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), time.Local)

str := temp.Format(shortForm)

return str

}



/*时间对象->时间戳*/

func Time2Stamp()int64{

t:=time.Now()

millisecond:=t.UnixNano()/1e6

return  millisecond

}

/*时间戳->字符串*/

func Stamp2Str(stamp int64) string{

timeLayout := "2006-01-02 15:04:05"

str:=time.Unix(stamp/1000,0).Format(timeLayout)

return str

}

/*时间戳->时间对象*/

func Stamp2Time(stamp int64)time.Time{

stampStr:=Stamp2Str(stamp)

timer:=Str2Time(stampStr)

return timer

}


Go数据库操作

database/sql包

初始化

database/sql标准库提供了用于处理sql相关的操作的接口,而接口的实现则交给了数据库驱动

好处在于写代码逻辑的时候,不用考虑后端的具体数据库,即使迁移数据库类型的时候,也只需要迁移相应的驱动即可,而不用修改代码

使用数据库时,除了database/sql包本身,必须要引入想使用的特定数据库驱动


database/sql的职责

通过驱动程序打开和关闭实际的底层数据库的连接

管理一个连接池


sql.Open()不建立与数据库的任何连接,也不会验证驱动连接参数,它只是准备数据库抽象以供以后使用

首次真正的连接数据库将在第一次需要时惰性建立

如果你想立即检查数据库是否可用(例如,是否可以建立网络连接、是否有权限登陆),请使用db.Ping()来执行此操作


sql.DB表示数据库抽象, 被设计为长期存在的对象

不要经常Open()和Close()数据库对象

应该在需要是把sql.DB对象作为参数,或赋值给全局变量


连接池配

db.SetMaxOpenConns(n int) 设置打开数据库的最大连接数

包括正在使用的连接和连接池中空闲的连接

如果申请一个连接时连接池已经没有了连接或者连接数达到了最大连接数,该调用将会被block,直到有可用的连接才会返回

设置这个值可以避免并发太高导致出现too many connections的错误

该函数的默认设置是0,表示无限制


db.SetMaxIdleConns(n int)设置连接池中保持的的最大空闲连接数

默认设置是0,表示连接池不会保持连接状态

即当连接释放回到连接池的时候,连接将会被关闭

这会导致连接再连接池中频繁的关闭和创建

保持空闲连接的存活会占用一定内存

MaxIdleConns应该始终小于或等于MaxOpenConns

更多的空闲连接数是没有意义的,因为最多也就能拿到所有打开的连接,剩余的空闲连接依然保持的空闲


db.SetConnMaxLifetime(t time.Duration) 设置连接被复用的最大时间

注意并不是连接空闲时间,而是从连接建立开始的时间

超过时限后连接就会被强制回收,目的是保证连接活性


db.SetConnMaxIdleTime(t time.Duration) 设置空闲连接保持的最大时间








Gin学习笔记

点击查看gin在线文档

安装

# 下载

go get -u github.com/gin-gonic/gin


# 初始化 go mod

go mod init project


Hello World

package main


// 设置路由引擎

func setupRouter() *gin.Engine {

    // 获取默认引擎 如果想要不适用默认中间件则 gin.New()

    r := gin.Default()

    // 拦截路由/ping 并处理

    r.GET("/ping", func(c *gin.Context) {

        c.String(200, "pong")

    })

    // 返回引擎

    return r

}


// 程序入口

func main() {

    r := setupRouter()

    // 运行在8080端口

    r.Run(":8080")

}


自动绑定

package main


import (

    "github.com/gin-gonic/gin"

)


type LoginForm struct {

    User     string `form:"user" binding:"required"`

    Password string `form:"password" binding:"required"`

}


func main() {

    router := gin.Default()

    router.POST("/login", func(c *gin.Context) {

        // 你可以使用显式绑定声明绑定 multipart form:

        // c.ShouldBindWith(&form, binding.Form)

        // 或者简单地使用 ShouldBind 方法自动绑定:

        var form LoginForm

        // 在这种情况下,将自动选择合适的绑定

        if c.ShouldBind(&form) == nil {

            if form.User == "user" && form.Password == "password" {

                c.JSON(200, gin.H{"status": "you are logged in"})

            } else {

                c.JSON(401, gin.H{"status": "unauthorized"})

            }

        } else {

                c.JSON(401, gin.H{"status": "missing params"})

        }

    })

    router.Run(":8080")

}


Query和表单数据获取

func main() {

    router := gin.Default()


    router.POST("/form_post", func(c *gin.Context) {

        // 按名字获取

        message := c.PostForm("message")

        // 获取一组值

        nick := c.DefaultPostForm("nick", "anonymous")

        // 返回json go默认返回最后一行 不需要return

        c.JSON(200, gin.H{

            "status":  "posted",

            "message": message,

            "nick":    nick,

        })

    })

    router.Run(":8080")

}


// 接收前端传过来的参数

ginServer.GET("/user/info", func(context *gin.Context) {

userid := context.Query("userid")

username := context.Query("username")

context.JSON(http.StatusOK, gin.H{

"userid":   userid,

"username": username,

})

})


轻量版

// 接收参数 轻量版

ginServer.GET("/user/info/:userid/:username", func(context *gin.Context) {

userid := context.Param("userid")

username := context.Param("username")

context.JSON(http.StatusOK, gin.H{

"userid":   userid,

"username": username,

})

})

1.png

路由匹配

func main() {

    router := gin.Default()


    // 此 handler 将匹配 /user/john 但不会匹配 /user/ 或者 /user

    router.GET("/user/:name", func(c *gin.Context) {

        name := c.Param("name")

        c.String(http.StatusOK, "Hello %s", name)

    })


    // 此 handler 将匹配 /user/john/ 和 /user/john/send

    // 如果没有其他路由匹配 /user/john,它将重定向到 /user/john/

    router.GET("/user/:name/*action", func(c *gin.Context) {

        name := c.Param("name")

        action := c.Param("action")

        message := name + " is " + action

        c.String(http.StatusOK, message)

    })


    router.Run(":8080")

}


路由组

func main() {

    router := gin.Default()


    // 简单的路由组: v1

    v1 := router.Group("/v1")

    {

        v1.POST("/login", loginEndpoint)

        v1.POST("/submit", submitEndpoint)

        v1.POST("/read", readEndpoint)

    }


    // 简单的路由组: v2

    v2 := router.Group("/v2")

    {

        v2.POST("/login", loginEndpoint)

        v2.POST("/submit", submitEndpoint)

        v2.POST("/read", readEndpoint)

    }


    router.Run(":8080")

}


重定向

// HTTP 重定向

r.GET("/test", func(c *gin.Context) {

    c.Redirect(http.StatusMovedPermanently, "http://www.google.com/")

})

// 路由重定向 text重定向到text2

r.GET("/test", func(c *gin.Context) {

    c.Request.URL.Path = "/test2"

    r.HandleContext(c)

})

r.GET("/test2", func(c *gin.Context) {

    c.JSON(200, gin.H{"hello": "world"})

})


// 404

ginServer.NoRoute(func(context *gin.Context) {

   context.HTML(http.StatusNotFound, "404.html", nil)

})


Gin主动发起请求

func main() {

    router := gin.Default()

    router.GET("/someDataFromReader", func(c *gin.Context) {

        response, err := http.Get("https://raw.githubusercontent.com/gin-gonic/logo/master/color.png")

        if err != nil || response.StatusCode != http.StatusOK {

            c.Status(http.StatusServiceUnavailable)

            return

        }

        

        // 获取响应体

        reader := response.Body

        // 获取相应长度

        contentLength := response.ContentLength

        // 获取响应头

        contentType := response.Header.Get("Content-Type")

    })

    router.Run(":8080")

}


中间件添加

func main() {

    // 新建一个没有任何默认中间件的路由

    r := gin.New()


    // 全局中间件

    // Logger 中间件将日志写入 gin.DefaultWriter,即使你将 GIN_MODE 设置为 release。

    r.Use(gin.Logger())



    // 你可以为每个路由添加任意数量的中间件。

    // MyBenchLogger是中间件 benchEndpoint是控制器

    r.GET("/benchmark", MyBenchLogger(), benchEndpoint)


    // 认证路由组

    // authorized := r.Group("/", AuthRequired())

    // 和使用以下两行代码的效果完全一样:

    authorized := r.Group("/")

    

    // 路由组中间件! 在此例中,我们在 "authorized" 路由组中使用自定义创建的 

    // AuthRequired() 中间件

    authorized.Use(AuthRequired())

    {

        authorized.POST("/login", loginEndpoint)

        authorized.POST("/submit", submitEndpoint)

        authorized.POST("/read", readEndpoint)


        // 嵌套路由组

        testing := authorized.Group("testing")

        testing.GET("/analytics", analyticsEndpoint)

    }


    // 监听并在 0.0.0.0:8080 上启动服务

    r.Run(":8080")

}


// 自定义一个 Go 中间件

func myHandler() gin.HandlerFunc {

return func(context *gin.Context) {

// 通过自定义的中间件,设置的值

// 在后续处理只要调用了这个中间件的事都可以拿到这里的参数

context.Set("userSession", "userid-1")

context.Next()  // 放行

context.Abort() // 阻断

}

}


// 注册中间件

ginServer.Use(myHandler())

// 接收前端传过来的参数

ginServer.GET("/user/info", myHandler(), func(context *gin.Context) {

// 取出中间件中的值

usersession := context.MustGet("userSession").(string)

log.Println("=========>", usersession)


userid := context.Query("userid")

username := context.Query("username")

context.JSON(http.StatusOK, gin.H{

"userid":   userid,

"username": username,

})

})



日志写入文件

func main() {

    // 禁用控制台颜色,将日志写入文件时不需要控制台颜色。

    gin.DisableConsoleColor()


    // 记录到文件。

    f, _ := os.Create("gin.log")

    gin.DefaultWriter = io.MultiWriter(f)


    // 如果需要同时将日志写入文件和控制台,请使用以下代码。

    // gin.DefaultWriter = io.MultiWriter(f, os.Stdout)


    router := gin.Default()

    router.GET("/ping", func(c *gin.Context) {

        c.String(200, "pong")

    })


    router.Run(":8080")

}


设置Cookie

import (

    "fmt"


    "github.com/gin-gonic/gin"

)


func main() {


    router := gin.Default()


    router.GET("/cookie", func(c *gin.Context) {


        cookie, err := c.Cookie("gin_cookie")


        if err != nil {

            cookie = "NotSet"

            c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)

        }


        fmt.Printf("Cookie value: %s \n", cookie)

    })


    router.Run()

}


加载静态页面,渲染页面


// 加载静态页面

ginServer.LoadHTMLGlob("templates/*")


// 响应index页面

ginServer.GET("/index", func(context *gin.Context) {

    context.HTML(http.StatusOK, "index.html", gin.H{

        "msg": "这是go后台传递来的数据",

    })

})


<!---  templates/index.html  -->

<!DOCTYPE html>

<html>

<head>

    <meta charset="UTF-8">

    <title>Title</title>

</head>

<body>

    <h1>感谢CCTV</h1>

    <p>获取的后端数据为{{ .msg }}</p>

</body>

</html>


PATH 参数处理

Path 路径中参数以:开头,如:/user/:name,匹配情况如下:

/user/123                匹配 123

/user/test                匹配 test

/user/123/test         不匹配

/user/                      不匹配


Path 路径中参数以*开头,如:/user/*action,匹配情况如下:

/user/123                匹配 /123

/user/test                匹配 /test

/user/123/test         匹配 /123/test

/user/                      匹配 /


gin访问和使用数据库

package main


import (

"database/sql"

"fmt"

_ "github.com/go-sql-driver/mysql"

"log"

)


func main() {

// 链接数据库

//dsn := "username:password@protocal(host:port)/dbname"

db, err := sql.Open("mysql", dsn)

if err != nil {

log.Fatalln(err.Error())

}


// 创建数据表

// person: id, name, age

/*

_, err = db.Exec("create table person(" +

"id int auto_increment primary key," +

"name varchar(12) not null," +

"age int default 1" +

");")

if err != nil {

log.Fatalln(err.Error())

}

*/


// 插入数据到数据库

/*

_, err = db.Exec("insert into person(name, age) values(?, ?),(?, ?);", "davie", 99, "dave2", 100)

if err != nil {

log.Fatalln(err.Error())

} else {

fmt.Println("数据插入成功。")

}

*/


// 查询数据库

rows, err := db.Query("select * from person;")

if err != nil {

log.Fatalln(err.Error())

}


scan:

if rows.Next() {

var person Person

if err := rows.Scan(&person.Id, &person.Name, &person.Age); err != nil {

log.Fatalln(err.Error())

}

fmt.Println(person)

goto scan

}


}

type Person struct {

Id int

Name string

Age int

}



beego学习入门

点击查看beego在线文档


一.beego是一个一体式的框架

Beego是一个快速开发Go应用的HTTP框架,可以用来快速开发API、Web及后端服务等各种应用,是一个RESTful的框架,相对于echo框架仅包含路由和控制器核心模块、Beego是一个完整的MVC框架包括路由&控制器、model、数据库ORM封装、view模板处理


二、安装Beego环境

2.1 安装Beego核心包

go get -u github.com/beego/beego/v2

2.2 安装orm包用于操作数据库,beego的orm包是独立的模块需要单独安装

go get github.com/beego/beego/v2/client/orm

2.3 安装mysql驱动, 必须安装mysql驱动,orm包才能工作

go get github.com/go-sql-driver/mysql

2.4 安装bee工具包,这个是beego开发的辅助工具,用于快速创建项目,运行项目以及打包项目

go get -u github.com/beego/bee/v2


三、使用bee创建项目

下面使用bee快速创建一个新的项目, 打开命令窗口,进入$GOPATH/src目录,然后执行bee命令。


bee new beegoProject

bee工具在当前目录创建一个beegoP


四、项目结构

beegoProject

├── conf            - 配置文件存放目录

│   └── app.conf    - beego应用配置文件,里面包含一些默认的配置包括启动端口、运行模式等等

├── controllers     - 控制器目录

│   └── default.go

├── main.go         - 入口文件

├── models          - model目录,存放我们的业务逻辑和数据库相关的操作

├── routers         - 路由配置目录,主要存放我们各个业务模块的路由设置

│   └── router.go

├── static          - 静态资源目录,默认静态资源访问url为 "http://域名/static/资源路径"

│   ├── css

│   ├── img

│   └── js

├── tests           - 单元测试脚本目录

│   └── default_test.go

└── views           - 视图模板目录

    └── index.tpl


五、运行项目

前面已经创建了项目,现在可以把项目运行起来看看效果。

通过bee工具运行项目,首先在命令创建进入“项目目录”。


bee run


六、控制器逻辑

我们看下beego控制器怎么写?

文件: beegoProject/controllers/default.go


package controllers


import (

beego "github.com/beego/beego/v2/server/web"

)


// 定义一个控制器结构体

// 我们一般一个模块定义一个控制器

type MainController struct {

    // 嵌套beego基础控制器,在go语言中嵌套struct,就类似继承的概念。

    // 这里就相当于,继承了beego.Controller的方法和属性。

beego.Controller 

}


// 覆盖beego.Controller的Get方法,用于处理RESTful请求中的get请求

// beego.Controller默认支持多种RESTful方法,例如:Post、Put、Delete等等

func (c *MainController) Get() {

    // Data是继承过来的属性,是map类型,可以保存任意类型数据,主要用于保存请求响应数据

    // 我们可以通过Data将参数,传入视图模板文件。

// 这里设置了两个参数

c.Data["Website"] = "tizi365.com"

c.Data["Email"] = "[email protected]"


// 设置需要渲染的模板文件,框架会去views目录查找这个模板文件

c.TplName = "index.tpl"

}

七、设置Url路由

下面看看怎么设置路由。


package routers


import (

"tizi365/controllers"

beego "github.com/beego/beego/v2/server/web"

)


// go 包初始化函数,go语言中在导入一个包的时候,如果被导入包存在init函数,会执行init函数

// 因此这里可以使用init函数初始化路由设置

func init() {

    // 使用beego.Router函数,注册路由规则。

    // 第一个参数是url路由,第二个参数是控制器

    // 这里的意思就是将访问 / 这个url的请求,交给controllers.MainController控制器处理。

    beego.Router("/", &controllers.MainController{})

}

如果我们增加下面路由设置:


beego.Router("/tizi365", &controllers.MainController{})

访问:http://localhost:8080/tizi365 和 http://localhost:8080/ 得到的结果一样,因为这两个url地址都是由同一个控制器处理。

路由规则说明:

大家可能发现上面的路由注册规则,只是定义了,那个url由那个控制器执行,但是没有说明url请求由控制器的那个函数执行, 一个控制器可以包含多个函数;


beego RESTful路由规则,默认是通过 请求方法 确认由那个控制器方法执行,例如get请求,由Get方法执行,POST请求由Post方法执行。


八、编写model逻辑

这里我们看一个mysql数据库操作的例子


8.1 定义表结构:

CREATE TABLE `users` (

  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',

  `username` varchar(30) NOT NULL COMMENT '账号',

  `password` varchar(100) NOT NULL COMMENT '密码',

   PRIMARY KEY (`id`)

  ) ENGINE=InnoDB DEFAULT CHARSET=utf8

大家可以使用自己熟悉的工具将mysql表创建好。


8.2 初始化数据库连接

一般初始化数据库连接都是在main.go入口地方是指一次就行,下面看下main.go文件改成什么样了。


package main


import (

beego "github.com/beego/beego/v2/server/web"

"github.com/beego/beego/v2/client/orm"

_ "tizi365/routers"

//导入mysql驱动,这是必须的

_ "github.com/go-sql-driver/mysql"

)


//初始化应用设置, 我们通过init函数初始化数据库连接,go语言中这个函数会优先执行

func init() {

    // 这里注册一个default默认数据库,数据库驱动是mysql.

    // 第三个参数是数据库dsn, 配置数据库的账号密码,数据库名等参数

    //  dsn参数说明:

    //      username    - mysql账号

    //      password    - mysql密码

    //      db_name     - 数据库名

    //      127.0.0.1:3306 - 数据库的地址和端口

orm.RegisterDataBase("default", "mysql", "username:password@tcp(127.0.0.1:3306)/db_name?charset=utf8")

}


func main() {

beego.Run()

}

为了初始化mysql连接,在入口main.go文件,增加init函数初始化数据库设置。


8.3 创建model

然后创建一个user model,文件路径:beegoProject/models/user.go,代码如下:


package models


import (

"github.com/beego/beego/v2/client/orm"

)


// 定义User模型,绑定users表结构, 其实就是用来保存sql查询结果。

type User struct {

Id int

Username string

Password string

}


// 定义User 模型绑定那个表?

func (u *User) TableName() string {

    // 返回mysql表名

return "users"

}


//初始化函数,可以用来向orm注册model

func init() {

    // 向orm注册user模型

orm.RegisterModel(&User{})

}


// 根据id查询用户信息

func GetUserById(id int) *User {

if  id == 0 {

return  nil

}

    

    // 创建orm对象, 后面都是通过orm对象操作数据库

o := orm.NewOrm()


// 初始化一个User模型对象

user := User{}

// 设置查询参数

user.Id = id


// 调用Read方法,根据user设置的参数,查询一条记录,结果保存到user结构体变量中

// 默认是根据主键进行查询

// 等价sql: SELECT `id`, `username`, `password` FROM `users` WHERE `id` = 1

err := o.Read(&user)


// 检测查询结果,

if err == orm.ErrNoRows {

// 找不到记录

return nil

} else if err == orm.ErrMissPK {

// 找不到住建

return nil

}


return &user

}

提示: 具体的orm用法,后续教程有详细说明,这里大概了解下大概的用法。

8.4 通过控制器调用model

下面修改控制器代码

文件:beegoProject/controllers/default.go


func (c *MainController) Get() {

c.Data["Website"] = "tizi365.com"

c.Data["Email"] = "[email protected]"


// 调用model,查询用户id为1 的用户信息

    user := models.GetUserById(1)


// 然后将user数据保存到Data中, 将参数传给后面的views视图模板处理

c.Data["user"] = user


// 使用新的视图模板user.tpl

c.TplName = "user.tpl"

}

九、编写view视图逻辑

这里编写一个新的视图模板, 代码如下:

文件: beegoProject/views/user.tpl


<!DOCTYPE html>

<html>

<head>

<title>Demo</title>

<meta charset="utf-8">

</head>

<body>

<h1>网站: {{.Website}}</h1>

{{ if .user }}

用户名: {{.user.Username}}

{{else}}

查找不到用户

{{ end }}

</body>

</html>

访问url: http://localhost:8080, 如果查询的用户存在,则显示用户名,否则显示查找不到用户。


提示: beego使用的是go自带的模板引擎,如果暂时看不懂,大家可以先忽略具体的模板语法,后续的教程会有讲解。

十、项目打包

项目完成后需要将代码打包发布到线上,这里依然推荐使用bee工具打包,bee工具可以一键将项目需要的相关文件一起打包成一个压缩包,只需要到线上解压即可。


下面是bee打包的例子, 首先将命令窗口的目录切换到 "项目根目录", 然后执行下面命令


bee pack

打包成功后再项目根目录下生成一个tizi365.tar.gz的压缩包,命名格式: ${项目名}.tar.gz


我们可以解压缩,看看压缩包包含什么内容:


beegoProject.tar.gz

├── conf            - 配置文件存放目录,这里包含我们的配置文件

├── static          - 静态资源目录,包含我们静态资源文件

├── views           - 视图模板目录,包含模板文件

└── beegoProject         - 这个就是我们的项目打包后的可执行程序,按我们项目命名 

注意: 线上服务器一般都是linux环境,所以建议在linux环境打包项目,不要在windows环境打包,否则打包生成的可执行程序是.exe格式的,linux无法运行。


Beego框架之bee工具


一、bee工具的简介

bee工具是一个为了协助快速开发beego项目而创建的项目,通过bee您可以很容易的进行beego项目的创建、热编译、开发、测试和部署。


1.1 工具安装

go get -u github.com/beego/bee/v2

安装完之后, bee 可执行文件默认存放在 $GOPATH/bin 里面,所以您需要把$GOPATH/bin 添加到您的环境变量中,才可以进行下一步。


如何添加环境变量,请自行搜索 如果你本机设置了 GOBIN ,那么上面的命令就会安装到GOBIN 下,请添加 GOBIN 到你的环境变量中

二、bee工具命令详解

我们在命令行输入bee,可以看到如下的信息:


Bee is a Fast and Flexible tool for managing your Beego Web Application.


You are using bee for beego v2.x. If you are working on beego v1.x, please downgrade version to bee v1.12.0


USAGE

    bee command [arguments]


AVAILABLE COMMANDS


    version     Prints the current Bee version

    migrate     Runs database migrations

    api         Creates a Beego API application

    bale        Transforms non-Go files to Go source files

    fix         Fixes your application by making it compatible with newer versions of Beego

    pro         Source code generator

    dev         Commands which used to help to develop beego and bee

    dlv         Start a debugging session using Delve

    dockerize   Generates a Dockerfile for your Beego application

    generate    Source code generator

    hprose      Creates an RPC application based on Hprose and Beego frameworks

    new         Creates a Beego application

    pack        Compresses a Beego application into a single file

    rs          Run customized scripts

    run         Run the application by starting a local development server

    server      serving static content over HTTP on port

    update      Update Bee


Use bee help [command] for more information about a command.

2.1 new命令

new 命令是新建一个 Web 项目,我们在命令行下执行 bee new <项目名> 就可以创建一个新的项目。但是注意该命令必须在 $GOPATH/src 下执行。最后会在 $GOPATH/src 相应目录下生成如下目录结构的项目:


# bee new myproject

[INFO] Creating application...

/gopath/src/myproject/

/gopath/src/myproject/conf/

/gopath/src/myproject/controllers/

/gopath/src/myproject/models/

/gopath/src/myproject/static/

/gopath/src/myproject/static/js/

/gopath/src/myproject/static/css/

/gopath/src/myproject/static/img/

/gopath/src/myproject/views/

/gopath/src/myproject/conf/app.conf

/gopath/src/myproject/controllers/default.go

/gopath/src/myproject/views/index.tpl

/gopath/src/myproject/main.go

13-11-25 09:50:39 [SUCC] New application successfully created!

tizi365

├── conf            - 配置文件存放目录

│   └── app.conf    - beego应用配置文件,里面包含一些默认的配置包括启动端口、运行模式等等

├── controllers     - 控制器目录

│   └── default.go

├── main.go         - 入口文件

├── models          - model目录,存放我们的业务逻辑和数据库相关的操作

├── routers         - 路由配置目录,主要存放我们各个业务模块的路由设置

│   └── router.go

├── static          - 静态资源目录,默认静态资源访问url为 "http://域名/static/资源路径"

│   ├── css

│   ├── img

│   └── js

├── tests           - 单元测试脚本目录

│   └── default_test.go

└── views           - 视图模板目录

    └── index.tpl

2.2 api命令

上面的 new 命令是用来新建 Web 项目,不过很多用户使用 beego 来开发 API 应用。所以这个 api 命令就是用来创建 API 应用的,执行命令之后如下所示:


# bee api apiProject

| ___ \

| |_/ /  ___   ___

| ___ \ / _ \ / _ \

| |_/ /|  __/|  __/

\____/  \___| \___| v2.0.2

2022/05/07 16:12:14 INFO     ? 0001 generate api project support go modules.

2022/05/07 16:12:14 INFO     ? 0002 Creating API...

        create   /home/ubuntu/go/src/githud.com/infodriven/apiProject/go.mod

        create   /home/ubuntu/go/src/githud.com/infodriven/apiProject

        create   /home/ubuntu/go/src/githud.com/infodriven/apiProject/conf

        create   /home/ubuntu/go/src/githud.com/infodriven/apiProject/controllers

        create   /home/ubuntu/go/src/githud.com/infodriven/apiProject/tests

        create   /home/ubuntu/go/src/githud.com/infodriven/apiProject/conf/app.conf

        create   /home/ubuntu/go/src/githud.com/infodriven/apiProject/models

        create   /home/ubuntu/go/src/githud.com/infodriven/apiProject/routers/

        create   /home/ubuntu/go/src/githud.com/infodriven/apiProject/controllers/object.go

        create   /home/ubuntu/go/src/githud.com/infodriven/apiProject/controllers/user.go

        create   /home/ubuntu/go/src/githud.com/infodriven/apiProject/tests/default_test.go

        create   /home/ubuntu/go/src/githud.com/infodriven/apiProject/routers/router.go

        create   /home/ubuntu/go/src/githud.com/infodriven/apiProject/models/object.go

        create   /home/ubuntu/go/src/githud.com/infodriven/apiProject/models/user.go

        create   /home/ubuntu/go/src/githud.com/infodriven/apiProject/main.go

2022/05/07 16:12:14 SUCCESS  ? 0003 New API successfully created!

apiproject

├── conf

│ └── app.conf

├── controllers

│ └── object.go

│ └── user.go

├── docs

│ └── doc.go

├── main.go

├── models

│ └── object.go

│ └── user.go

├── routers

│ └── router.go

└── tests

└── default_test.go

从上面的目录我们可以看到和 Web 项目相比,少了 static 和 views 目录,多了一个 test 模块,用来做单元测试的。

同时,该命令还支持一些自定义参数自动连接数据库创建相关 model 和 controller: bee api[appname] [-tables=""] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] 如果conn 参数为空则创建一个示例项目,否则将基于链接信息链接数据库创建项目。


2.3 run命令

我们在开发 Go 项目的时候最大的问题是经常需要自己手动去编译再运行, bee run 命令是监控beego 的项目,通过 fsnotify监控文件系统。但是注意该命令必须在 $GOPATH/src/appname下执行。 这样我们在开发过程中就可以实时的看到项目修改之后的效果:


bee run

13-11-25 09:53:04 [INFO] Uses 'myproject' as 'appname'

13-11-25 09:53:04 [INFO] Initializing watcher...

13-11-25 09:53:04 [TRAC] Directory(/gopath/src/myproject/controllers)

13-11-25 09:53:04 [TRAC] Directory(/gopath/src/myproject/models)

13-11-25 09:53:04 [TRAC] Directory(/gopath/src/myproject)

13-11-25 09:53:04 [INFO] Start building...

13-11-25 09:53:16 [SUCC] Build was successful

13-11-25 09:53:16 [INFO] Restarting myproject ...

13-11-25 09:53:16 [INFO] ./myproject is running...

我们打开浏览器就可以看到效果 http://localhost:8080/

如果我们修改了 Controller 下面的 default.go 文件,我们就可以看到命令行输出:


13-11-25 10:11:20 [EVEN] "/gopath/src/myproject/controllers/default.go":

DELETE|MODIFY

13-11-25 10:11:20 [INFO] Start building...

13-11-25 10:11:20 [SKIP] "/gopath/src/myproject/controllers/default.go": CREATE

13-11-25 10:11:23 [SKIP] "/gopath/src/myproject/controllers/default.go": MODIFY

13-11-25 10:11:23 [SUCC] Build was successful

13-11-25 10:11:23 [INFO] Restarting myproject ...

13-11-25 10:11:23 [INFO] ./myproject is running...

刷新浏览器我们看到新的修改内容已经输出。


2.4 pack 命令

pack 目录用来发布应用的时候打包,会把项目打包成 zip 包,这样我们部署的时候直接把打包之后的项目上传,解压就可以部署了:


bee pack

app path: /gopath/src/apiproject

GOOS darwin GOARCH amd64

build apiproject

build success

exclude prefix:

exclude suffix: .go:.DS_Store:.tmp

file write to `/gopath/src/apiproject/apiproject.tar.gz`

我们可以看到目录下有如下的压缩文件:


1.  rwxr-xr-x 1 astaxie staff 8995376 11 25 22:46 apiproject

2.  -rw-r--r-- 1 astaxie staff 2240288 11 25 22:58 apiproject.tar.gz

3.  drwxr-xr-x 3 astaxie staff 102 11 25 22:31 conf

4.  drwxr-xr-x 3 astaxie staff 102 11 25 22:31 controllers

5.  -rw-r--r-- 1 astaxie staff 509 11 25 22:31 main.go

6.  drwxr-xr-x 3 astaxie staff 102 11 25 22:31 models

7.  drwxr-xr-x 3 astaxie staff 102 11 25 22:31 tests

2.5 bale 命令

这个命令目前仅限内部使用,具体实现方案未完善,主要用来压缩所有的静态文件变成一个变量申明文

件,全部编译到二进制文件里面,用户发布的时候携带静态文件,包括 js、css、img 和 views。

最后在启动运行时进行非覆盖式的自解压。


2.6 version 命令

这个命令是动态获取 bee、beego 和 Go 的版本,这样一旦用户出现错误,可以通过该命令来查看当前的版本


$ bee version

bee :1.2.2

beego :1.4.2

Go :go version go1.3.3 darwin/amd64

2.7 generate 命令

这个命令是用来自动化的生成代码的,包含了从数据库一键生成 model,还包含了 scaffold 的,通过这个命令,让大家开发代码不再慢


bee generate scaffold [scaffoldname] [-fields=""] [-driver=mysql] [-

1.  conn="root:@tcp(127.0.0.1:3306)/test"]

2.  The generate scaffold command will do a number of things for you.

3.  -fields: a list of table fields. Format: field:type, ...

4.  -driver: [mysql | postgres | sqlite], the default is mysql

-conn: the connection string used by the driver, the default is

5.  root:@tcp(127.0.0.1:3306)/test

6.  example: bee generate scaffold post -fields="title:string,body:text"

7.

8.  bee generate model [modelname] [-fields=""]

9.  generate RESTful model based on fields

10.  -fields: a list of table fields. Format: field:type, ...

11.

12.  bee generate controller [controllerfile]

13.  generate RESTful controllers

14.

15.  bee generate view [viewpath]

16.  generate CRUD view in viewpath

17.

18.  bee generate migration [migrationfile] [-fields=""]

19.  generate migration file for making database schema update

20.  -fields: a list of table fields. Format: field:type, ...

21.

22.  bee generate docs

23.  generate swagger doc file

24.

25.  bee generate test [routerfile]

26.  generate testcase

27.

28.

bee generate appcode [-tables=""] [-driver=mysql] [-

conn="root:@tcp(127.0.0.1:3306)/test"] [-level=3]

29.  generate appcode based on an existing database

30.

-tables: a list of table names separated by ',', default is empty,

indicating all tables

31.  -driver: [mysql | postgres | sqlite], the default is mysql

32.  -conn: the connection string used by the driver.

33.  default for mysql: root:@tcp(127.0.0.1:3306)/test

34.

default for postgres:

postgres://postgres:[email protected]:5432/postgres

35.

-level: [1 | 2 | 3], 1 = models; 2 = models,controllers; 3 =

models,controllers,router

2.8 migrate 命令

这个命令是应用的数据库迁移命令,主要是用来每次应用升级,降级的SQL管理。


1.  bee migrate [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]

2.  run all outstanding migrations

3.  -driver: [mysql | postgresql | sqlite], the default is mysql

4.

-conn: the connection string used by the driver, the default is

root:@tcp(127.0.0.1:3306)/test

5.

6.  bee migrate rollback [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]

7.  rollback the last migration operation

8.  -driver: [mysql | postgresql | sqlite], the default is mysql

9.

-conn: the connection string used by the driver, the default is

root:@tcp(127.0.0.1:3306)/test

10.

11.  bee migrate reset [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]

12.  rollback all migrations

13.  -driver: [mysql | postgresql | sqlite], the default is mysql

14.

-conn: the connection string used by the driver, the default is

root:@tcp(127.0.0.1:3306)/test

15.

16.  bee migrate refresh [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]

17.  rollback all migrations and run them all again

18.  -driver: [mysql | postgresql | sqlite], the default is mysql

19.

-conn: the connection string used by the driver, the default is

root:@tcp(127.0.0.1:3306)/test

2.9 dockerize 命令

这个命令可以通过生成Dockerfile文件来实现docker化你的应用。

例子:

生成一个以1.6.4版本Go环境为基础镜像的Dockerfile,并暴露9000端口:


1.  $ bee dockerize -image="library/golang:1.6.4" -expose=9000

2.  ______

3.  | ___ \

4.  | |_/ / ___ ___

5.  | ___ \ / _ \ / _ \

6.  | |_/ /| __/| __/

7.  \____/ \___| \___| v1.6.2

8.  2016/12/26 22:34:54 INFO ? 0001 Generating Dockerfile...

9.  2016/12/26 22:34:54 SUCCESS ? 0002 Dockerfile generated.

更多帮助信息可执行 bee help dockerize .


三、bee工具配置文件

您可能已经注意到,在 bee 工具的源码目录下有一个 bee.json 文件,这个文件是针对 bee 工

具的一些行为进行配置。该功能还未完全开发完成,不过其中的一些选项已经可以使用:


"version": 0 :配置文件版本,用于对比是否发生不兼容的配置格式版本。

"go_install": false :如果您的包均使用完整的导入路径(例如: github.com/user/repo/subpkg ),则可以启用该选项来进行 go install 操作,加快构建操作。

"watch_ext": [] :用于监控其它类型的文件(默认只监控后缀为 .go 的文件)。

"dir_structure":{} :如果您的目录名与默认的 MVC 架构的不同,则可以使用该选项进行修改。

"cmd_args": [] :如果您需要在每次启动时加入启动参数,则可以使用该选项。

"envs": [] :如果您需要在每次启动时设置临时环境变量参数,则可以使用该选项 




Beego框架之参数配置 

beego 默认使用了 INI 格式解析配置文件,通常在项目中会存在很多系统参数、业务参数配置,这些参数通常都是通过配置文件进行配置,而且不是写死在代码里面。


提示:例如mysql账号密码之类的系统参数,如果写死在代码里面,每次修改参数都得重新打包升级,非常不灵活。

提示:修改配置文件后,需要重启应用,配置才生效,即使使用bee run运行项目也得重启。

一、beego系统参数

我先介绍下beego自带的系统参数有哪些?默认情况,conf/app.conf就是我们的默认配置文件。

例子:


# 这是注释

#应用名称

appname = tizi356

#http 服务端口

httpport = 8080

#运行模式,常用的运行模式有dev, test, prod

runmode = dev

下面表格是beego常用配置:


提示: 参数名不区分大小写, 下面的参数配置,了解下即可,需要的时候再查。

参数名 默认值 说明

AppName beego 应用名

RunMode dev 程序运行模式,常用模式有dev、test、prod,一般用于区分不同的运行环境

RouterCaseSensitive true 是否路由忽略大小写匹配

ServerName beego beego 服务器默认在请求的时候输出 server 头的值。

RecoverPanic true 是否异常恢复,默认值为 true,即当应用出现异常的情况,通过 recover 恢复回来,而不会导致应用异常退出。

EnableGzip false 是否开启 gzip 支持

MaxMemory 64M 文件上传默认内存缓存大小,单位是字节

AutoRender true 是否模板自动渲染,对于 API 类型的应用,应用需要把该选项设置为 false,不需要渲染模板。

StaticDir static 静态文件目录设置

ViewsPath views 模板路径

Graceful false 是否开启热升级,默认是 false,关闭热升级。

ServerTimeOut 0 设置 HTTP 的超时时间,默认是 0,不超时。

HTTPAddr 应用监听地址,默认为空,监听所有的网卡 IP。

HTTPPort 8080 应用监听端口

EnableHTTPS false 是否启用 HTTPS,默认是 false 关闭。当需要启用时,先设置 EnableHTTPS = true,并设置 HTTPSCertFile 和 HTTPSKeyFile

HTTPSAddr https应用监听地址,默认为空,监听所有的网卡 IP。

HTTPSPort 10443 https应用监听端口

HTTPSCertFile 开启 HTTPS 后,ssl 证书路径

HTTPSKeyFile 开启 HTTPS 之后,SSL 证书 keyfile 的路径。

EnableAdmin false 是否开启进程内监控模块,默认 false 关闭。

AdminAddr localhost 监控程序监听的地址。

AdminPort 8088 监控程序监听的地址。

SessionOn false session 是否开启

SessionProvider memory session 的引擎, 详情参考session章节的教程

SessionName beegosessionID 存在客户端的 cookie 名称。

SessionGCMaxLifetime 3600 session 过期时间, 单位秒。

SessionProviderConfig 配置信息,根据不同的session引擎设置不同的配置信息,详细的配置请参考session章节的教程

SessionCookieLifeTime 3600 session 默认存在客户端的 cookie 的时间, 单位秒。

SessionDomain session cookie 存储域名。

二、自定义参数

除了beego系统自带的配置,我们也可以自定义配置,然后通过beego.AppConfig对象的函数读取配置。


例子:

我们在app.conf增加下面自定义配置


# 下面是关于mysql数据库的配置参数

mysql_user = "root"

mysql_password = "123456"

mysql_host = "127.0.0.1:3306"

mysql_dbname = "tizi365"

下面是读取配置代码:


web.AppConfig.String("mysql_user")

web.AppConfig.String("mysql_password")

web.AppConfig.String("mysql_host")

web.AppConfig.String("mysql_dbname")

beego.AppConfig对象,为我们定义了一些常用的函数,用于读取配置,下面列出一些常用的函数:


函数名 说明

String 以字符串的方式返回参数

Int 以int类型的方式返回参数

Int64 以Int64类型的方式返回参数

Bool 以Bool类型的方式返回参数

Float 以Float类型的方式返回参数

提示:以上函数,只有一个参数,就是配置的名字

如果配置项的参数为空,希望返回默认值,可以使用下面的函数:


函数名 说明

DefaultString 以字符串的方式返回参数

DefaultInt 以int类型的方式返回参数

DefaultInt64 以Int64类型的方式返回参数

DefaultBool 以Bool类型的方式返回参数

DefaultFloat 以Float类型的方式返回参数

提示: 以上函数,只有两个参数,第一个参数是配置项名字,第二个参数是默认值

例子:


// 如果mysql_port配置项的参数为空,则返回3306

web.AppConfig.DefaultInt("mysql_port", 3306)

三、不同运行级别的参数

前面提到runmode参数可以设置不同的运行级别,我们一般用来区分不用的运行环境,例如: dev、test等等。

如果我们希望数据库配置在不同环境,账号密码都不一样,可以使用如下配置方式:

例子:


# 配置运行级别

runmode ="dev"


[dev]

mysql_user = "root"

mysql_password = "123456"

mysql_host = "127.0.0.1:3306"

mysql_dbname = "tizi365"


[test]

mysql_user = "root"

mysql_password = "Ihd9ay86asgk"

mysql_host = "61.99.21.1:3306"

mysql_dbname = "tizi365"


[prod]

mysql_user = "root"

mysql_password = "8hlabdias986"

mysql_host = "202.12.91.1:3306"

mysql_dbname = "tizi365"

上面的例子,我们为dev,test,prod三个环境配置了不同的数据库参数,当我们通过web.AppConfig读取参数的时候,由runmode决定读取那个环境的参数。 例如:当runmode=test, mysql_password=Ihd9ay86asgk


四、使用多个配置文件

在实际项目中,我们一般都使用多个配置文件管理配置,多个配置文件也方便我们模块化管理配置。


例如: 我们新建一个mysql.conf配置文件,保存数据库配置。

文件: conf/mysql.conf


[dev]

mysql_user = "root"

mysql_password = "123456"

mysql_host = "127.0.0.1:3306"

mysql_dbname = "tizi365"

然后我们在conf/app.conf主配置文件中,通过include 将mysql配置文件包含进去。


AppName = tizi356

HttpPort = 8080

runmode = dev


# 包含mysql配置

include "mysql.conf"

这种通过include包含其他配置文件的方式,跟把所有配置都写在一个配置文件的效果是一样的, 区别就是使用多个配置文件,各个模块的配置更加清晰。


五、支持环境便里昂配置

到目前为止,我们的配置参数都是通过ini配置文件进行配置,如果想通过环境变量进行配置怎么办?尤其是在docker容器环境运行,通常都需要通过环境变量配置应用参数。


beego支持优先从环境变量中读取参数, 只要在ini配置文件中通过 ${环境变量名},定义配置项的值。


例子:


runmode  = "${APP_RUN_MODE || dev}"

httpport = "${APP_PORT || 9090}"

上面例子的意思就是:

如果环境变量APP_RUN_MODE值不为空,runmode配置的参数就等于APP_RUN_MODE环境变量的值,如果为空,则使用dev作为默认参数。 同理APP_PORT为空,则使用9090作为默认值,否则使用APP_PORT的值。


Beego控制器之路由配置


路由指的就是一个url请求由谁来处理,在beego设计中,url请求可以由控制器的函数来处理,也可以由一个单独的函数来处理,因此路由设置由两部分组成:URL路由和处理函数。

beego提供两种设置处理函数的方式:


直接绑定一个函数

绑定一个控制器对象(RESTful方式)

一、直接绑定处理函数

这种方式直接将一个url路由和一个函数绑定起来。

例子:


// 这就是将url / 和一个闭包函数绑定起来, 这个url的Get请求由这个闭包函数处理。

web.Get("/",func(ctx *context.Context){

     ctx.Output.Body([]byte("hi tizi365.com"))

})


// 定义一个处理函数

func Index(ctx *context.Context){

     ctx.Output.Body([]byte("欢迎访问 tizi365.com"))

}


// 注册路由, 将url /index 和Index函数绑定起来,由Index函数处理这个url的Post请求

web.Post("/index", Index)

下面是beego支持的基础函数:


web.Get(router, web.FilterFunc)

web.Post(router, web.FilterFunc)

web.Put(router, web.FilterFunc)

web.Patch(router, web.FilterFunc)

web.Head(router, web.FilterFunc)

web.Options(router, web.FilterFunc)

web.Delete(router, web.FilterFunc)

web.Any(router, web.FilterFunc) - 处理任意http请求,就是不论请求方法(Get,Post,Delete等等)是什么,都由绑定的函数处理

根据不同的http请求方法(Get,Post等等)选择不同的函数设置路由即可


二、RESTful路由方式

RESTful 是一种目前比较流行的url风格,beego默认支持这种风格。

在beego项目中,RESTful路由方式就是将url路由跟一个控制器对象绑定,然后Get请求由控制的Get函数处理,Post请求由Post函数处理,以此类推。

RESTful路由使用web.Router函数设置路由。

例子:


// url: / 的所有http请求方法都由MainController控制器的对应函数处理

web.Router("/", &controllers.MainController{})


// url: /user 的所有http请求方法都由UserController控制器的对应函数处理

// 例如: GET /user请求,由Get函数处理, POST /user 请求,由Post函数处理

web.Router("/user", &controllers.UserController{})

三、url路由方式

上面介绍设置处理函数的方式,下面介绍beego支持的url路由方式。


提示:下面介绍的url路由规则,都是用于上面介绍的所有路由设置函数。

3.1 固定路由

前面介绍的url路由例子,都属于固定路由方式,固定路由指的是url规则是固定的一个url。

例子:


web.Router("/user", &controllers.UserController{})

web.Router("/shop/order", &controllers.OrderController{})

web.Router("/shop/comment", &controllers.CommentController{})

3.2 正则路由

正则路由比较灵活,一个正则路由设置代表的是一序列的url, 正则路由更像是一种url模板。

url路由例子:


/user/:id

匹配/user/132,参数 :id=132

/user/:id([0-9]+)

匹配/user/123,参数 :id=123, 跟上面例子的区别就是只能匹配数字

/user/:username([\w]+)

匹配/user/tizi, 参数 :username=tizi

/list_:cat([0-9]+)_:page([0-9]+).html

匹配/list_2_1.html, 参数 :cat=2, :page=1

/api/*

匹配/api为前缀的所有url, 例子: /api/user/1 , 参数: :splat=user/1

在 Controller 对象中,可以通过下面的方式获取url路由匹配的参数:


func (c *MainController) Get() {

c.Ctx.Input.Param(":id")

c.Ctx.Input.Param(":username")

c.Ctx.Input.Param(":cat")

c.Ctx.Input.Param(":page")

c.Ctx.Input.Param(":splat")

}

3.3 自动路由

自动路由指的是通过反射获取到控制器的名字和控制器实现的所有函数名字,自动生成url路由。

使用自动路由首先需要beego.AutoRouter函数注册控制器。

例子:


web.AutoRouter(&controllers.UserController{})

url自动路由例子:


/user/login   调用 UserController 中的 Login 方法

/user/logout  调用 UserController 中的 Logout 方法

除了前缀两个 /:controller/:method 的匹配之外,剩下的 url beego 会帮你自动化解析为参数,保存在 this.Ctx.Input.Params 当中:


/user/list/2019/09/11  调用 UserController 中的 List 方法,参数如下:map[0:2019 1:09 2:11]

提示:自动路由会将url和控制器名字、函数名字转换成小写。

3.4 namespace

路由名字空间(namespace),一般用来做api版本处理。

例子:


// 创建版本1的名字空间

ns1 := web.NewNamespace("/v1",

    // 内嵌一个/user名字空间

    web.NSNamespace("/user",

        // 下面开始注册路由

        // url路由: /v1/user/info

        web.NSRouter("/info", &controllers.UserController{}),

        // url路由: /v1/user/order

        web.NSRouter("/order", &controllers.UserOrderController{}),

    ),

    // 内嵌一个/shop名字空间

    web.NSNamespace("/shop",

        // 下面开始注册路由

        // url路由: /v1/shop/info

        web.NSRouter("/info", &controllers.ShopController{}),

        // url路由: /v1/shop/order

        web.NSRouter("/order", &controllers.ShopOrderController{}),

    ),

)


// 创建版本2的名字空间

ns2 := web.NewNamespace("/v2",

    web.NSNamespace("/user",

        // url路由: /v2user/info

        web.NSRouter("/info", &controllers.User2Controller{}),

    ),

    web.NSNamespace("/shop",

        // url路由: /v2/shop/order

        web.NSRouter("/order", &controllers.ShopOrder2Controller{}),

    ),

)


//注册 namespace

web.AddNamespace(ns1)

web.AddNamespace(ns2)

通过NewNamespace函数创建多个名字空间,NSNamespace函数可以无限嵌套名字空间, 根据上面的例子可以看出来,名字空间的作用其实就是定义url路由的前缀,如果一个名字空间定义url路由为/user, 那么这个名字空间下面定义的所有路由的前缀都是以/user开头。


下面是namespace支持的路由设置函数:


NewNamespace(prefix string, funcs …interface{})

NSNamespace(prefix string, funcs …interface{})

NSInclude(cList …ControllerInterface)

NSRouter(rootpath string, c ControllerInterface, mappingMethods …string)

NSGet(rootpath string, f FilterFunc)

NSPost(rootpath string, f FilterFunc)

NSDelete(rootpath string, f FilterFunc)

NSPut(rootpath string, f FilterFunc)

NSHead(rootpath string, f FilterFunc)

NSOptions(rootpath string, f FilterFunc)

NSPatch(rootpath string, f FilterFunc)

NSAny(rootpath string, f FilterFunc)

NSHandler(rootpath string, h http.Handler)

NSAutoRouter(c ControllerInterface)

NSAutoPrefix(prefix string, c ControllerInterface)

这些路由设置函数的参数,跟前面的路由设置函数一样,区别就是namespace的函数名前面多了NS前缀 


Beego控制器之控制器函数

控制器函数指的是处理用户请求的函数,前面路由设置章节介绍过,beego框架支持两种处理用户请求的函数。


beego.FilterFunc类型的独立函数

控制器函数(RESTful风格实现,beego默认推荐的格式)

一、beego.FilterFunc函数

这是最简单的请求处理函数,函数原型定义:


type FilterFunc func(*context.Context)

也就是只要定义一个函数,并且接收一个Context参数,那么这个函数就可以作为处理用户请求的函数。

例子:


func DoLogin(ctx *context.Context) {

     // ..处理请求的逻辑...

     // 可以通过Context 获取请求参数,返回请求结果

}

有了处理函数,我们就可以将处理函数跟一个url路由绑定起来.

例子:


web.Get("/user/login", DoLogin)

提示: 新版的beego设计,默认不推荐使用beego.FilterFunc函数方式,这种方式处理请求的方式比较原始,后面介绍的控制器函数拥有更多高级特性,后续的教程以控制器函数为主。

二、控制器函数

控制器函数是beego的RESTful api的实现方式,在beego的设计中,控制器就是一个嵌套了beego.Controller的结构体对象。

例子:


// 定义一个新的控制器

type UserController struct {

    // 嵌套beego基础控制器

    web.Controller

}

前面介绍过,struct嵌套,就类似其他高级语言的 继承 特性,嵌套了web.Controller控制器,就拥有了web.Controller定义的属性和函数。


控制器命名规则约定:XxxController

Xxx就是我们的控制器名字, 这是为了便于阅读,看到Controller结尾的struct就知道是一个控制器。


下面看一个完整控制器的例子:


type UserController struct {

    // 嵌套beego基础控制器

    web.Controller

}


// 在调用其他控制器函数之前,会优先调用Prepare函数

func (this *UserController) Prepare() {

    // 这里可以跑一些初始化工作

}


// 处理get请求

func (this *UserController) Get() {

    // 处理逻辑

}


// 处理post请求

func (this *UserController) Post() {

    // 处理逻辑

}

注册路由


// 在这里参数:id是可选的

web.Router("/user/?:id", &controllers.UserController{})

根据上面注册的路由规则, 下面的展示对应的http请求和处理函数:


GET /user/2 - 由Get函数处理

POST /user - 由Post函数处理

提示:前面路由设置章节介绍过控制器的路由规则是Get请求由Get函数处理,Post请求由Post函数处理,以此类推。

下表展示了web.Controller默认为我们提供了哪些可选的函数:


提示: 根据业务需要,控制器可以覆盖下表中的函数。

函数名 说明

Prepare() 这个函数会优先执行,才会执行Get、Post之类的函数, 可以在Prepare做一些初始化工作。

Get() 处理get请求, 如果没有实现该函数,默认会返回405错误。

Post() 处理Post请求, 默认会返回405错误。

Delete() 处理Delete请求, 默认会返回405错误。

Put() 处理PUT请求, 默认会返回405错误。

Finish() 执行完Get、Post之类http请求函数之后执行,我们可以在Finish函数处理一些回收工作。

三、如何提前结束请求

如果我们在Prepare函数处理用户的权限验证,验证不通过,我们一般都希望结束请求,不要执行后面的函数,beego提供了StopRun函数来结束请求。

例子:


func (this *UserController) Prepare() {

    // 处理权限验证逻辑


    // 验证不通过,返回错误信息,结束请求

    this.Data["json"] = map[string]interface{}{"error":"没有权限", "errno":401}

this.ServeJSON()

    this.StopRun()

}

提示:调用 StopRun 之后,不会再执行Finish函数,如果有需要可以在调用StopRun之后,手动调用Finish函数。 


Beego控制器之获取请求参数

web.Controller基础控制器,为我们提供了多种读取请求参数的函数,下面分别介绍各种获取参数的场景。


一、默认获取参数方式

web.Controller基础控制器为我们提供了GetXXX序列获取参数的函数, XXX指的就是返回不同的数据类型。

例子:


// 处理get请求

func (this *UserController) Get() {

// 获取参数, 返回int类型

id ,_:= this.GetInt("id")

// 获取参数,返回string类型, 如果参数不存在返回none作为默认值

username := this.GetString("username", "none")

// 获取参数,返回float类型, 参数不存在则返回 0

price, _ := this.GetFloat("price", 0)

}

下面是常用的获取参数的函数定义:


GetString(key string, def ...string) string

GetInt(key string, def ...int) (int, error)

GetInt64(key string, def ...int64) (int64, error)

GetFloat(key string, def ...float64) (float64, error)

GetBool(key string, def ...bool) (bool, error)

默认情况用户请求的参数都是 字符串 类型,如果要转换成其他类型,就可能会出现类型转换失败的可能性,因此除了GetString函数,其他GetXXX函数,都返回两个值,第一个值是需要获取的参数值,第二个就是error,表示是数据类型转换是否失败。


二、绑定struct方式

除了上面一个一个的获取请求参数,针对POST请求的表单数据,beego支持直接将表单数据绑定到一个struct变量。

例子:


// 定义一个struct用来保存表单数据

// 通过给字段设置tag, 指定表单字段名, - 表示忽略这个字段不进行赋值

// 默认情况下表单字段名跟struct字段名同名(小写)

type UserForm struct {

    // 忽略掉Id字段

    Id    int         `form:"-"`

    // 表单字段名为username

    Name  string      `form:"username"`

    Phone string

}

说明: 如果表单字段跟struct字段(小写)同名,不需要设置form标签。 表单html代码:

<form action="/user" method="POST">

    手机号:<input name="phone" type="text" /><br/>

    用户名:<input name="username" type="text" />

    <input type="submit" value="提交" />

</form>

控制器函数:


func (this *UserController) Post() {

    // 定义保存表单数据的struct对象

    u := UserForm{}

    // 通过ParseForm函数,将请求参数绑定到struct变量。

    if err := this.ParseForm(&u); err != nil {

        // 绑定参数失败

    }

}

提示:使用struct绑定请求参数的方式,仅适用于POST请求。

三、处理json请求参数

一般在接口开发的时候,有时候会将json请求参数保存在http请求的body里面。我们就不能使用前的方式获取json数据,需要直接读取请求body的内容,然后格式化数据。

处理json参数的步骤:


在app.conf配置文件中,添加CopyRequestBody=true

通过this.Ctx.Input.RequestBody获取请求body的内容

通过json.Unmarshal反序列化json字符串,将json参数绑定到struct变量。

例子:

定义struct用于保存json数据


// 如果json字段跟struct字段名不一样,可以通过json标签设置json字段名

type UserForm struct {

    // 忽略掉Id字段

    Id    int         `json:"-"`

    // json字段名为username

    Name  string      `json:"username"`

    Phone string      

}

控制器代码:


func (this *UserController) Post() {

    // 定义保存json数据的struct对象

    u := UserForm{}

    

    // 获取body内容

    body := this.Ctx.Input.RequestBody

    

    // 反序列json数据,结果保存至u

    if err := json.Unmarshal(body, &u); err == nil {

        // 解析参数失败

    }

}

提示: 如果将请求参数是xml格式,xml参数也是保存在body中,处理方式类似,就是最后一步使用xml反序列化函数进行处理。 


Beego控制器之响应请求

我们处理完用户的请求之后,通常我们都会返回html代码,然后浏览器就可以显示html内容;除了返回html,在api接口开发中,我们还可以返回json、xml、jsonp格式的数据。

下面分别介绍beego返回不同数据类型的处理方式。


注意:如果使用beego开发api,那么在app.conf中设置AutoRender = false, 禁止自动渲染模板,否则beego每次处理请求都会尝试渲染模板,如果模板不存在则报错。

一、返回json数据

下面是返回json数据的例子:


// 定义struct

// 如果struct字段名跟json字段名不一样,可以使用json标签,指定json字段名

type User struct {

    // - 表示忽略id字段

Id       int `json:"-"`

Username string `json:"name"`

Phone    string

}


func (this *UserController) Get() {

    // 定义需要返回给客户端的数据

    user := User{1, "tizi365", "13089818901"}

    

    // 将需要返回的数据赋值给json字段

    this.Data["json"] = &user

    

    // 将this.Data["json"]的数据,序列化成json字符串,然后返回给客户端

    this.ServeJSON()

}

提示:请参考Go处理json数据教程,了解详细的json数据处理方式。

二、返回XML数据

下面是返回xml数据的处理方式跟json类似。

例子:


// 定义struct

// 如果struct字段名跟xml字段名不一样,可以使用xml标签,指定xml字段名

type User struct {

    // - 表示忽略id字段

Id       int `xml:"-"`

Username string `xml:"name"`

Phone    string

}


func (this *UserController) Get() {

    // 定义需要返回给客户端的数据

    user := User{1, "tizi365", "13089818901"}

    

    // 将需要返回的数据赋值给xml字段

    this.Data["xml"] = &user

    

    // 将this.Data["xml"]的数据,序列化成xml字符串,然后返回给客户端

    this.ServeXML()

}

提示:请参考Go处理xml数据教程,了解详细的xml数据处理方式。

三、返回jsonp数据

返回jsonp数据,于返回json数据方式类似。

例子:


func (this *UserController) Get() {

    // 定义需要返回给客户端的数据

    user := User{1, "tizi365", "13089818901"}

    

    // 将需要返回的数据赋值给jsonp字段

    this.Data["jsonp"] = &user

    

    // 将this.Data["json"]的数据,序列化成json字符串,然后返回给客户端

    this.ServeJSONP()

}

4.返回html

如果我们开发的是网页,那么通常需要返回html代码,在beego项目中关于html视图部分,使用的是模板引擎技术,渲染html,然后将结果返回给浏览器。

例子:


func (c *MainController) Get() {

    // 设置模板参数

c.Data["Website"] = "tizi365.com"

c.Data["Email"] = "[email protected]"

// 需要渲染的模板, beego会渲染这个模板,然后返回结果

c.TplName = "index.tpl"

}

5.添加响应头

为http请求添加header


func (c *MainController) Get() {

    // 通过this.Ctx.Output.Header设置响应头

    this.Ctx.Output.Header("Content-Type", "message/http")

    this.Ctx.Output.Header("Cache-Control", "no-cache, no-store, must-revalidate")


ProtoBuf入门教程

在网络通信和通用数据交换等应用场景中经常用的技术是JSON和XML,本教程介绍另外一个数据交换的协议工具ProtoBuf。


一、简介

protocol buffers (ProtoBuf)是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。

Protocol Buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。

json\xml都是基于文本格式,protobuf是二进制格式。

你可以通过 ProtoBuf 定义数据结构,然后通过 ProtoBuf 工具生成各种语言版本的数据结构类库,用于操作 ProtoBuf 协议数据

本教程介绍的是最新的protobuf proto3版本的语法。


二、使用ProtoBuf的例子

2.1 创建.proto文件,定义数据结构

使用 ProtoBuf ,首先需要通过 ProtoBuf 语法定义数据结构(消息),这些定义好的数据结构保存在.proto为后缀的文件中。

例子:

文件名: response.proto


// 指定protobuf的版本,proto3是最新的语法版本

syntax = "proto3";


// 定义数据结构,message 你可以想象成java的class,c语言中的struct

message Response {

  string data = 1;   // 定义一个string类型的字段,字段名字为data, 序号为1

  int32 status = 2;   // 定义一个int32类型的字段,字段名字为status, 序号为2

}

说明:proto文件中,字段后面的序号,不能重复,定义了就不能修改,可以理解成字段的唯一ID。

2.2 安装ProtoBuf编译器

protobuf的github发布地址: https://github.com/protocolbuffers/protobuf/releases

protobuf的编译器叫protoc,在上面的网址中找到最新版本的安装包,下载安装。

这里下载的是:protoc-3.9.1-win64.zip , windows 64位系统版本的编译器,下载后,解压到你想要的安装目录即可。


提示:安装完成后,将 [protoc安装目录]/bin 路径添加到PATH环境变量中

打开cmd,命令窗口执行protoc命令,没有报错的话,就已经安装成功。


2.3 vscode安装ProtoBuf插件

vscode-proto3

2.4 将.proto文件,编译成指定语言类库

protoc编译器支持将proto文件编译成多种语言版本的代码,我们这里以java为例。

切换到proto文件所在的目录, 执行下面命令


protoc --java_out=. response.proto

然后在当前目录生成了一个ResponseOuterClass.java的java类文件,这个就是我们刚才用protobuf语法定义的数据结构对应的java类文件,通过这个类文件我们就可以操作定义的数据结构。


2.6 在代码中使用ProtoBuf对数据进行序列化和反序列化

因为上面的例子使用的是java, 我们先导入protobuf的基础类库。

maven:


<dependency>

            <groupId>com.google.protobuf</groupId>

            <artifactId>protobuf-java</artifactId>

            <version>3.9.1</version>

</dependency>

使用ProtoBuf的例子。


ResponseOuterClass.Response.Builder builder = ResponseOuterClass.Response.newBuilder();

// 设置字段值

builder.setData("hello www.tizi365.com");

builder.setStatus(200);


 ResponseOuterClass.Response response = builder.build();

 // 将数据根据protobuf格式,转化为字节数组

 byte[] byteArray  = response.toByteArray();


// 反序列化,二进制数据

try {

    ResponseOuterClass.Response newResponse = ResponseOuterClass.Response.parseFrom(byteArray);

    System.out.println(newResponse.getData());

    System.out.println(newResponse.getStatus());

} catch (Exception e) {

 } 


(二)ProtoBuf定义消息

消息(message),在protobuf中指的就是我们定义的数据结构。


一、语法

syntax = "proto3";


message 消息名 {

    消息体

}

syntax关键词定义使用的是proto3语法版本,如果没有指定默认使用的是proto2。

message关键词,标记开始定义一个消息,消息体,用于定义各种字段类型。


提示: protobuf消息定义的语法结构,跟我们平时接触的各种语言的类定义,非常相似。

例子:


syntax = "proto3";


message SearchRequest {

  string query = 1;

  int32 page_number = 2;

  int32 result_per_page = 3;

}

定义了一个SearchRequest消息,这个消息有3个字段,query是字符串类型,page_number和result_per_page是int32类型。


提示:我们通常将protobuf消息定义保存在.proto为后缀的文件中。

二、字段类型

支持多种数据类型,例如:string、int32、double、float等等,下一章节会详细介绍


三、分配标识号

通过前面的例子,在消息定义中,每个字段后面都有一个唯一的数字,这个就是标识号。

这些标识号是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改变,每个消息内唯一即可,不同的消息定义可以拥有相同的标识号。


注意:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。切记:要为将来有可能添加的、频繁出现的字段预留一些标识号。

保留标识号(Reserved)

如果你想保留一些标识号,留给以后用,可以使用下面语法:


message Foo {

  reserved 2, 15, 9 to 11; // 保留2,15,9到11这些标识号

}

如果使用了这些保留的标识号,protocol buffer编译器会输出警告信息。


四、注解

往.proto文件添加注释,支持C/C++/java风格的双斜杠(//) 语法格式。

例子:


// 定义SearchRequest消息

message SearchRequest {

  string query = 1;

  int32 page_number = 2;  // 页码

  int32 result_per_page = 3;  // 分页大小

}

五、为消息定义包

我们也可以为消息定义包。

例子:


package foo.bar;

message Open { ... }

定义了一个包:foo.bar


六、选项

下面是一些常用的选项:


java_package # 单独为java定义包名字。

java_outer_classname # 单独为java定义,protobuf编译器生成的类名。

将消息编译成各种语言版本的类库

编译器命令格式:

protoc [OPTION] PROTO_FILES

OPTION是命令的选项, PROTO_FILES是我们要编译的proto消息定义文件,支持多个。

常用的OPTION选项:


  --cpp_out=OUT_DIR           指定代码生成目录,生成 C++ 代码

  --csharp_out=OUT_DIR        指定代码生成目录,生成 C# 代码

  --java_out=OUT_DIR          指定代码生成目录,生成 java 代码

  --js_out=OUT_DIR            指定代码生成目录,生成 javascript 代码

  --objc_out=OUT_DIR          指定代码生成目录,生成 Objective C 代码

  --php_out=OUT_DIR           指定代码生成目录,生成 php 代码

  --python_out=OUT_DIR        指定代码生成目录,生成 python 代码

  --ruby_out=OUT_DIR          指定代码生成目录,生成 ruby 代码

例子:


protoc --java_out=. demo.proto

在当前目录导出java版本的代码,编译demo.proto消息。

有些语言需要单独安装插件才能编译proto,例如golang

安装go语言的protoc编译器插件


go get -u github.com/golang/protobuf/protoc-gen-go

注意: 安装go语言插件后,需要将 $GOPATH/bin 路径加入到PATH环境变量中。

编译成go语言版本


protoc --go_out=. helloworld.proto


(三)ProtoBuf数据类型

Protobuf定义了一套基本数据类型,下表罗列出了protobuf类型和其他语言类型的映射表。

1.png

(四)ProtoBuf枚举(enum)类型

当需要定义一个消息类型的时候,可能想为一个字段指定“预定义值序列”中的一个值,这时候可以通过枚举实现。


例子:


syntax = "proto3";//指定版本信息,不指定会报错


enum PhoneType //枚举消息类型,使用enum关键词定义,一个电话类型的枚举类型

{

    MOBILE = 0; //proto3版本中,首成员必须为0,成员不应有相同的值

    HOME = 1;

    WORK = 2;

}


// 定义一个电话消息

message PhoneNumber

{

    string number = 1; // 电话号码字段

    PhoneType type = 2; // 电话类型字段,电话类型使用PhoneType枚举类型

}


(五)ProtoBuf数组类型 

在protobuf消息中定义数组类型,是通过在字段前面增加repeated关键词实现,标记当前字段是一个数组。


一、整数数组的例子:

message Msg {

  // 只要使用repeated标记类型定义,就表示数组类型。

  repeated int32 arrays = 1;

}

二、字符串数组

message Msg {

  repeated string names = 1;

}


(六)ProtoBuf消息嵌套

我们在各种语言开发中类的定义是可以互相嵌套的,也可以使用其他类作为自己的成员属性类型。

在protobuf中同样支持消息嵌套,可以在一个消息中嵌套另外一个消息,字段类型可以是另外一个消息类型。


一、引用其他消息类型的用法

// 定义Result消息

message Result {

  string url = 1;

  string title = 2;

  repeated string snippets = 3; // 字符串数组类型

}


// 定义SearchResponse消息

message SearchResponse {

  // 引用上面定义的Result消息类型,作为results字段的类型

  repeated Result results = 1; // repeated关键词标记,说明results字段是一个数组

}

二、消息嵌套

类似类嵌套一样,消息也可以嵌套。

例子:


message SearchResponse {

  // 嵌套消息定义

  message Result {

    string url = 1;

    string title = 2;

    repeated string snippets = 3;

  }

  // 引用嵌套的消息定义

  repeated Result results = 1;

}

三、import导入其他proto文件定义的消息

我们在开发一个项目的时候通常有很多消息定义,都写在一个proto文件,不方便维护,通常会将消息定义写在不同的proto文件中,在需要的时候可以通过import导入其他proto文件定义的消息。

例子:

保存文件: result.proto


syntax = "proto3";

// Result消息定义

message Result {

  string url = 1;

  string title = 2;

  repeated string snippets = 3; // 字符串数组类型

}

保存文件: search_response.proto


syntax = "proto3";

// 导入Result消息定义

import "result.proto";


// 定义SearchResponse消息

message SearchResponse {

  // 使用导入的Result消息

  repeated Result results = 1; 

}


(七)ProtoBuf map类型 

protocol buffers支持map类型定义。


一、map语法

map<key_type, value_type> map_field = N;

key_type可以是任何整数或字符串类型(除浮点类型和字节之外的任何标量类型)。请注意,枚举不是有效的key_type。

value_type 可以是除另一个映射之外的任何类型。


二、map的例子

syntax = "proto3";

message Product

{

    string name = 1; // 商品名

    // 定义一个k/v类型,key是string类型,value也是string类型

    map<string, string> attrs = 2; // 商品属性,键值对

}


(八)Golang如何使用ProtoBuf 

本节介绍,在go语言中,如何是用protobuf对数据进行序列化和反序列化。


一、先参考protobuf快速入门章节安装protoc编译器

protoc快速入门


二、安装protoc-gen-go

安装针对go语言的编译器插件。


go get -u github.com/golang/protobuf/protoc-gen-go

安装好了之后, 在$GOPATH/bin下面会找到protoc-gen-go,编译器插件,将$GOPATH/bin路径添加到PATH环境变量中。


三、安装protobuf包

使用protobuf需要先安装对应的包。


go get -u github.com/golang/protobuf

四、定义proto消息

例子:

文件名:score_server/score_info.proto


syntax = "proto3";

option go_pacakge="./;score_server"

//option go_package="path;name"

//path表示生成go文件的存放地址,会自动生成目录。name表示生成的go文件所属包名。


package score_server;



// 基本的积分消息

message base_score_info_t{

    int32       win_count = 1;                  // 玩家胜局局数

    int32       lose_count = 2;                 // 玩家负局局数

    int32       exception_count = 3;            // 玩家异常局局数

    int32       kill_count = 4;                 // 总人头数

    int32       death_count = 5;                // 总死亡数

    int32       assist_count = 6;               // 总总助攻数

    int64       rating = 7;                     // 评价积分

}

五、编译proto文件,生成go代码

cd score_server

protoc --go_out=. score_info.proto

六、测试代码

package main


import (

"fmt"

        // 导入protobuf依赖包

"github.com/golang/protobuf/proto"

       // 导入我们刚才生成的go代码所在的包,注意你们自己的项目路径,可能跟本例子不一样

"demo/score_server" 

)


func main() {

        // 初始化消息

score_info := &score_server.BaseScoreInfoT{}

score_info.WinCount = 10

score_info.LoseCount = 1

score_info.ExceptionCount = 2

score_info.KillCount = 2

score_info.DeathCount = 1

score_info.AssistCount = 3

score_info.Rating = 120


// 以字符串的形式打印消息

fmt.Println(score_info.String())


// encode, 转换成二进制数据

data, err := proto.Marshal(score_info)

if err != nil {

panic(err)

}


// decode, 将二进制数据转换成struct对象

new_score_info := score_server.BaseScoreInfoT{}

err = proto.Unmarshal(data, &new_score_info)

if err != nil {

panic(err)

}


// 以字符串的形式打印消息

fmt.Println(new_score_info.String())

}


Top