Go 的beego 框架

beego 和 gin  框架的对比:

https://www.jianshu.com/p/bb93fdaf30c7

Beego在业务方面较Gin支持更多

  • 在业务更加复杂的项目,适用beego
  • 在需要快速开发的项目,适用beego
  • 在1.0的项目中,适用beego,因为项目初期对性能没太大要求

Gin在性能方面较beego更好

  • 当某个接口性能遭到较大的挑战,考虑用Gin重写
  • 如果项目的规模不大,业务相对简单,适用Gin

beego  和  bee的安装:

安装bee 的时候会有坑

    一.go版本
        确保go版本在1.11及以上。
    二.创建项目文件夹,并初始化
        cd /project
        mkdir test
        cd test
        go mod init projectName(自定义的项目名)
    三.替换bee的源
        1. 首先我在 github.com/beego/bee 项目上 fork到我自己的项目下
            git地址为: git@github.com:****/bee.git
        2. 在执行完 go mod init 命令后,会在当前目录生成一个 go.mod 文件
        编辑文件如下:(注 要把git地址 git@和.git部分去掉,并把:号替换成/)
                module beego
                replace github.com/beego/bee v1.10.0 => github.com/***/bee v1.6.2
                go 1.12
    四. 正式安装beego和bee
        1. export GOPROXY=https://goproxy.io
        2. go get -u github.com/astaxie/beego
        3. go get -u github.com/beego/bee
        如果中间没报错,即安装成功
    五. 将bee命令放到GOROOT/bin目录下,这步很关键
        cp bee /usr/local/go/bin/
          注:或者可以将GOPATH/bin设置为环境变量
          echo ’export PATH="$GOPATH/bin:$PATH"' >> ~/.bashrc
          source ~/.bashrc
    六. 测试
        cd /project/test
        bee new hello
        cd src/hello
        go mod init hello(不执行)
        bee run
        即可看到程序已启动,访问8080端口即可看到beego的页面
View Code

打开本地8080 端口,

Beego 的基础:

官方文档十分详细: https://beego.me/docs/intro/   

Beego HelloWorld :

package main

import "github.com/astaxie/beego"

type MainController struct {
    beego.Controller
}

func (this *MainController) Get()  {
    this.Ctx.WriteString("Hello world")
}

func main()  {
    beego.Router("/",&MainController{})
    beego.Run()
}
View Code

Beego NameSpace :

package routers

import (
    "github.com/astaxie/beego"
    "quickstart/controllers"
)

func init() {

    ns := beego.NewNamespace("/v1",
        beego.NSNamespace("/user",
            beego.NSRouter("/login",&controllers.MainController{})))

    beego.AddNamespace(ns)
}
View Code

MVC :

M : 数据库 

V:模板

C:路由控制 

Beego Orm :

安装:go get github.com/astaxie/beego/orm

增:

package main

import (
    "fmt"
    "github.com/astaxie/beego/orm"
    _ "github.com/go-sql-driver/mysql"
    "main.go/models"

    _ "main.go/models"
)

func init()  {
    fmt.Println("main.go init")
    orm.RegisterDataBase("default","mysql", "root:123456@/go_mysql?charset=utf8")
    orm.RunSyncdb("default",false,true)
}
// 增
func insert(o orm.Ormer)  {
    //=================insert====================
    // 创建 profile
    //var profile models.Profile
    //profile.Age = 38
    //profileId,err := o.Insert(&profile)
    //if err != nil {
    //    fmt.Println("insert profile failed,err:",err)
    //}
    //fmt.Println(profileId)
    //
    //// 创建 user
    //var user models.User
    //user.Name = "xxx"
    //user.Profile = &profile
    //userId,err := o.Insert(&user)
    //if err != nil {
    //    fmt.Println("insert user failed,err:",err)
    //}
    //fmt.Println(userId)


    //=================insert multi====================
    //profiles := []models.Profile{
    //    {Age: 10},
    //    {Age: 11},
    //    {Age: 12},
    //    {Age: 13},
    //}
    //successNums, err := o.InsertMulti(1, profiles) // bulk 为1 将会顺序插入 slice 中的数据
    //if err != nil {
    //    fmt.Println("failed,err",err)
    //}
    //fmt.Println("success num :",successNums)


    //=================PrepareInsert====================
    //用于一次 prepare 多次 insert 插入,以提高批量插入的速度。
    //profiles := []models.Profile{{Age: 17},{Age: 16},{Age: 15},}
    //qs := o.QueryTable("profile")
    //i,_ := qs.PrepareInsert()
    //for _,profile := range profiles{
    //    id,_ := i.Insert(&profile)
    //    fmt.Println("inserted success",id)
    //}
    //i.Close() // 别忘记关闭 statement
}



func main()  {
    o := orm.NewOrm() // 默认使用的是 default 数据库

        // 增        
    insert(o)



}
main.go
package models

import (
    "github.com/astaxie/beego/orm"
    "fmt"
)
// 用户
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))
    fmt.Println("models.go init")

}
models/model.go

查:

models/model.go 同上,

package main

import (
    "fmt"
    "github.com/astaxie/beego/orm"
    _ "github.com/go-sql-driver/mysql"
    "main.go/models"

    _ "main.go/models"
)

func init()  {
    fmt.Println("main.go init")
    orm.RegisterDataBase("default","mysql", "root:123456@/go_mysql?charset=utf8")
    orm.RunSyncdb("default",false,true)
}
// 查
func query(o orm.Ormer)  {
    //=================Read====================
    //user := models.User{Id: 1}
    //err := o.Read(&user)
    //if err == orm.ErrNoRows {
    //    fmt.Println("查询不到")
    //} else if err == orm.ErrMissPK {
    //    fmt.Println("找不到主键")
    //} else {
    //    fmt.Println(user.Id, user.Name)
    //}

    //user := models.User{Name: "jack"}
    //err := o.Read(&user,"Name") // Read 默认通过查询主键赋值,可以使用指定的字段进行查询
    //if err != nil {
    //    fmt.Println("failed",err)
    //}
    //fmt.Println(user)


    //=================ReadOrCreate====================
    //默认必须传入一个参数作为条件字段
    //profile := models.Profile{Age: 20}
    //// 三个返回参数依次为:是否新创建的,对象 Id 值,错误
    //created, id, err := o.ReadOrCreate(&profile, "Age")
    //if err != nil {
    //    fmt.Println("failed,",err)
    //}else{
    //    if created {
    //        fmt.Println("New Insert an object. Id:", id)
    //    } else {
    //        fmt.Println("Get an object. Id:", id)
    //    }
    //}


    //=================高级查询====================
    //=================高级查询====================
    //=================高级查询====================
    //ORM 以 QuerySeter 来组织查询,每个返回 QuerySeter 的方法都会获得一个新的 QuerySeter 对象。

    //================= 获取 QuerySeter====================
    // 获取 QuerySeter 对象,user 为表名
    //qs := o.QueryTable("user")

    //// 也可以直接使用对象作为表名
    //user := new(models.User)
    //qs = o.QueryTable(user) // 返回 QuerySeter


    //=================expr====================
    //QuerySeter 中用于描述字段和 sql 操作符,使用简单的 expr 查询方法
    //字段组合的前后顺序依照表的关系,比如 User 表拥有 Profile 的外键,那么对 User 表查询对应的 Profile.Age 为条件,则使用 Profile__Age 注意,字段的分隔符号使用双下划线 __,除了描述字段, expr 的尾部可以增加操作符以执行对应的 sql 操作。比如 Profile__Age__gt 代表 Profile.Age > 18 的条件查询。
    //注释后面将描述对应的 sql 语句,仅仅是描述 expr 的类似结果,并不代表实际生成的语句。
    qs := o.QueryTable("user")

    //=================Filter====================
    //用来过滤查询结果,起到 包含条件 的作用
    //多个 Filter 之间使用的是 AND 连接  不是 OR的关系

    //var users []models.User
    //qs.Filter("id",1).All(&users)
    //qs.Filter("profile__lt",3).All(&users) // WHERE profile_id < 3 // profile_id 数据库中存的字段
    //qs.Filter("profile__age",38).All(&users) // WHERE profile.age = 38
    //qs.Filter("profile__age__gt",18).All(&users) // WHERE profile.age > 18
    //qs.Filter("profile__age__gte",18).All(&users) // WHERE profile.age >= 18
    //qs.Filter("profile__age__in",18,28).All(&users) //WHERE profile.age IN (18, 28)
    //fmt.Println(users)


    //=================Exclude====================
    //用来过滤查询结果,起到 排除条件 的作用
    //使用 NOT 排除条件
    //多个 Exclude 之间使用 AND 连接

    //var users []models.User
    //qs.Exclude("profile__lt",3).All(&users)
    //fmt.Println(users)

    //=================Limit====================
    //限制最大返回数据行数,第二个参数可以设置 Offset // ORM 默认的 limit 值为 1000
    //var users []models.User
    //qs.Filter("id__gte",1).Limit(2).All(&users)
    //fmt.Println(users)

    //var users []models.User
    //qs.Filter("id__gte",1).Limit(2,1).All(&users) // offset 为1
    //fmt.Println(users)


    //=================Offset====================
    //var users []models.User
    //qs.Filter("id__gte",1).Offset(1).All(&users) // offset 为1
    //fmt.Println(users)

    //=================GroupBy(没有不知道如何用,还是原生sql吧)====================
    //var users []models.User
    //qs.GroupBy("name").All(&users)
    //fmt.Println(users)

    //var maps []orm.Params
    //o.Raw("select name,avg(profile_id) as avg from user group by name").Values(&maps)
    //fmt.Println(maps)
    //for _,m := range maps{
    //    fmt.Println(m)
    //}

    //=================OrderBy====================
    //在 expr 前使用减号 - 表示 DESC 的排列
    //var users []models.User
    //qs.OrderBy("profile_id").All(&users)
    //fmt.Println(users)
    //
    //
    //qs.OrderBy("-profile_id").All(&users)
    //fmt.Println(users)


    //=================Distinct====================
    //var users []models.User
    //qs.Distinct().All(&users,"name")
    //fmt.Println(users)

    //=================Count====================
    //依据当前的查询条件,返回结果行数
    //cnt, _ := qs.Count()
    //fmt.Println(cnt)

    //cnt,_ := qs.Filter("name","xxx").Count()
    //fmt.Println(cnt)

    //=================Exist====================
    //ret1 := qs.Filter("name","yyy").Exist()
    //ret2 := qs.Filter("name","zzz").Exist()
    //ret3 := qs.Filter("name","xxx").Exist()
    //fmt.Println(ret1,ret2,ret3)


    //=================All====================
    //All / Values / ValuesList / ValuesFlat 受到 Limit 的限制,默认最大行数为 1000
    //可以指定返回的字段:
    //var users []models.User
    //qs.All(&users,"id","name") // 指定返回id 和 name 对象的其他字段值将会是对应类型的默认值
    //fmt.Println(users)

    //=================One====================
    //尝试返回单条记录
    //var users []models.User
    //qs.Filter("name","xxx").One(&users) // 如果有多条也是返回一个  One 后面也可以指定返回字段
    //fmt.Println(users)

    //=================Values====================
    // 返回结果以 map 存储
    //var maps []orm.Params // 其实是 [] map[string]interface{}
    //cnt,err := qs.Values(&maps)
    //if err != nil {
    //    fmt.Println("failed,err",err)
    //}
    //fmt.Println(cnt)
    //fmt.Println(maps)
    //for _,m :=range maps{
    //    fmt.Println(m)
    //}


    //暂不支持级联查询 RelatedSel 直接返回 Values
    //但可以直接指定 expr 级联返回需要的数据
    //var maps []orm.Params // 其实是 [] map[string]interface{}
    //cnt,err := qs.Values(&maps,"id", "name", "profile", "profile__age")
    //if err != nil {
    //    fmt.Println("failed,err",err)
    //}
    //for _,m :=range maps{
    //    fmt.Println(m["Id"],m["Name"],m["Profile"],m["Profile__Age"])
    //}

    //=================ValuesList====================
    //返回的结果集以slice存储
    //结果的排列与 Model 中定义的 Field 顺序一致
    //返回的每个元素值以 string 保存
    //var lists []orm.ParamsList
    //cnt ,err := qs.ValuesList(&lists)
    //if err != nil {
    //    fmt.Println("failed",err)
    //}
    //fmt.Println(cnt)
    //fmt.Println(lists)
    //for _,l := range lists{
    //    fmt.Println(l[0],l[1],l[2])
    //}


    //可以指定 expr 返回指定的 Field
    //var lists []orm.ParamsList
    //cnt ,err := qs.ValuesList(&lists,"name", "profile__age")
    //if err != nil {
    //    fmt.Println("failed",err)
    //}
    //fmt.Println(cnt)
    //for _,l := range lists{
    //    fmt.Println(l[0],l[1])
    //}

    //=================ValuesFlat====================
    //只返回特定的 Field 值,将结果集展开到单个 slice 里
    //var l orm.ParamsList
    //cnt,err := qs.ValuesFlat(&l,"name")
    //if err != nil {
    //    fmt.Println("failed",err)
    //}
    //fmt.Println(cnt)
    //fmt.Println(l)


    //=================Operators====================
    //=================当前支持的操作符号====================
    //当前支持的操作符号:
    //exact / iexact 等于
    //contains / icontains 包含
    //gt / gte 大于 / 大于等于
    //lt / lte 小于 / 小于等于
    //startswith / istartswith 以…起始
    //endswith / iendswith 以…结束
    //in
    //isnull
    //后面以 i 开头的表示:大小写不敏感

    /*exact*/
    // 它是 Filter / Exclude / Condition 的默认值 // 就是等于
    //使用 = 匹配,大小写是否敏感取决于数据表使用的 collation
    //qs.Filter("name", "tom") // WHERE name = 'tom'
    //qs.Filter("name__exact", "tom") // WHERE name = 'tom'
    //qs.Filter("profile_id", nil) // WHERE profile_id IS NULL

    /*iexact*/
    // 大小写不敏感,匹配任意 'tom' 'Tom'
    //qs.Filter("name__iexact", "tom") // WHERE name LIKE 'slene'

    /*contains*/
    // 大小写敏感, 匹配包含 o 的字符
    //qs.Filter("name__contains", "o") // WHERE name LIKE BINARY '%o%'

    /*icontains*/
    // 大小写不敏感, 匹配包含 o 的字符
    //qs.Filter("name__icontains", "o")

    /*in*/
    //var users []models.User
    //qs.Filter("name__in", "tom","xxx").All(&users) // WHERE age IN ( "tom","xxx")
    //fmt.Println(users)

    // 同上效果
    //var users []models.User
    //names :=[]string{"tom","xxx"}
    //qs.Filter("name__in", names).All(&users) // WHERE age IN ( "tom","xxx")
    //fmt.Println(users)

    /*gt / gte*/  // 大于 大于等于
    /*lt / lte*/  // 小于 小于等于


    /*startswith*/ // 大小写敏感,
    /*istartswith*/ // 大小写不敏感,
    /*endswith*/ // 大小写敏感,
    /*iendswith*/ // 大小写不敏感,


    /* isnull */
    //qs.Filter("profile__isnull", true)
    //qs.Filter("profile_id__isnull", true)
    //// WHERE profile_id IS NULL
    //
    //qs.Filter("profile__isnull", false)
    //// WHERE profile_id IS NOT NULL


    //=================SetCond====================
    //自定义条件表达式
    //var users []models.User
    //cond := orm.NewCondition()
    //                    // age >= 28  and profile_id <= 3 or age = 38
    //cond1 := cond.And("profile__age__gte",28).AndNot("profile__gt",3).Or("profile__age",38)
    //qs.SetCond(cond1).All(&users) // WHERE ... AND ... AND NOT ... OR ...
    //fmt.Println(users)
    //
    //cond2 := cond.AndCond(cond1).OrCond(cond.And("name", "jack")) // WHERE (... AND ... AND NOT ... OR ...) OR ( ... )
    //qs.SetCond(cond2).All(&users)
    //fmt.Println(users)


}

func main()  {
    o := orm.NewOrm() // 默认使用的是 default 数据库
    query(o)
}
main.go

改:

models/model.go 同上,

package main

import (
    "fmt"
    "github.com/astaxie/beego/orm"
    _ "github.com/go-sql-driver/mysql"
    "main.go/models"

    _ "main.go/models"
)

func init()  {
    fmt.Println("main.go init")
    orm.RegisterDataBase("default","mysql", "root:123456@/go_mysql?charset=utf8")
    orm.RunSyncdb("default",false,true)
}
func update(o orm.Ormer)  {

    //=================Update====================
    //第一个返回值为影响的行数
    //user := models.User{Id: 1}
    //err := o.Read(&user)
    //if err != nil {
    //    fmt.Println("failed",err)
    //    return
    //}
    //user.Name="tomman"
    //cnt,_ := o.Update(&user)
    //fmt.Println(cnt)

    //Update 默认更新所有的字段,可以更新指定的字段:
    //user := models.User{Id: 1}
    //err := o.Read(&user)
    //if err != nil {
    //    fmt.Println("failed",err)
    //    return
    //}
    //user.Name="tomman"
    //cnt,_ := o.Update(&user,"Name")
    //fmt.Println(cnt)

    //=================批量更新====================
    //依据当前查询条件,进行批量更新操作
    //num, err := o.QueryTable("user").Filter("id__in", 1,2,3).Update(orm.Params{  //
    //    "name": "tom",
    //})
    //fmt.Printf("Affected Num: %d, %s", num, err)


    //=================原子操作增加字段值====================
    // 假设 user struct 里有一个 nums int 字段
    //num, err := o.QueryTable("user").Update(orm.Params{
    //    "nums": orm.ColValue(orm.ColAdd, 100),
    //})
    // SET nums = nums + 100

    //orm.ColValue 支持以下操作
    //ColAdd      ////ColMinus    ////ColMultiply ////ColExcept   // 除
}


func main()  {
    o := orm.NewOrm() // 默认使用的是 default 数据库

    // 改
    update(o)


}
main.go

删: 

models/model.go 同上,

package main

import (
    "fmt"
    "github.com/astaxie/beego/orm"
    _ "github.com/go-sql-driver/mysql"
    "main.go/models"

    _ "main.go/models"
)

func init()  {
    fmt.Println("main.go init")
    orm.RegisterDataBase("default","mysql", "root:123456@/go_mysql?charset=utf8")
    orm.RunSyncdb("default",false,true)
}


func del(o orm.Ormer)  {


    //=================Delete====================
    //第一个返回值为影响的行数
    //profile := models.Profile{Id: 5}
    //cnt,_ := o.Delete(&profile)
    //fmt.Println(cnt)

    //Delete 操作会对反向关系进行操作,
    //此例中 Post 拥有一个到 User 的外键。删除 User 的时候。如果 on_delete 设置为默认的级联操作,将删除对应的 Post
    //Changed in 1.0.3 删除以后不会删除 auto field 的值

    //=================批量删除====================
    //num, err := o.QueryTable("profile").Filter("id__in", 7,8,9).Delete()
    //fmt.Printf("Affected Num: %d, %s", num, err)
}



func main()  {
    o := orm.NewOrm() // 默认使用的是 default 数据库
    //del(o)


}
main.go

一对一,一对多,多对多,以及 载入关系字段 以及  QueryM2Mer 对象

models/model.go 同上,

package main

import (
    "fmt"
    "github.com/astaxie/beego/orm"
    _ "github.com/go-sql-driver/mysql"
    "main.go/models"

    _ "main.go/models"
)

func init()  {
    fmt.Println("main.go init")
    orm.RegisterDataBase("default","mysql", "root:123456@/go_mysql?charset=utf8")
    orm.RunSyncdb("default",false,true)
}

func main()  {
    o := orm.NewOrm() // 默认使用的是 default 数据库

    // 关系操作
    //=================一对一====================
    //=================一对一====================
    //=================一对一====================
    // User 和 Profile
    // 新增一对一
    //profile := models.Profile{Age: 18}
    //o.Insert(&profile)
    //user := models.User{Name: "jaxxx",Profile: &profile}
    //o.Insert(&user)

    // 一对一查询
    // 任务 查询 用户id为1 的 profile


    //=================正向查询====================
    // (已经取得了 User 对象,查询 Profile)
    //user := models.User{Id: 1}
    //o.Read(&user)
    //fmt.Println(user)
    ////fmt.Println(user.Profile) // 此时是user 表中的profile_id
    //o.Read(user.Profile) // 查询
    ////fmt.Println(user.Profile) // 此时是profile 表中的 数据了

    // 直接关联 查询
    //user := models.User{}
    //o.QueryTable("user").Filter("Id", 1).RelatedSel().One(&user)
    //fmt.Println(user.Profile) // 此时已经是profile 表中的 数据了
    //
    //// 因为在 Profile 里定义了反向关系的 User,所以 Profile 里的 User 也是自动赋值过的,可以直接取用。
    //fmt.Println(user.Profile.User)
    //fmt.Println(user)


    //=================反向查询====================
    // 通过profile 反向查询 User
    //var profile models.Profile
    //o.QueryTable("profile").Filter("User__Id",1).One(&profile)
    //fmt.Println(profile)








    //=================一对多====================
    //=================一对多====================
    //=================一对多====================
    // User 和 Post
    // 新增一对多
    //user := models.User{Id: 1}
    //post := models.Post{Title: "Golang从入门到放弃",User: &user}
    //o.Insert(&post)

    // 一对多查询
    // 任务 查询 用户id为1 的 post
    //=================正向查询====================
    //user := models.User{Id: 1}
    //o.Read(&user)
    //o.LoadRelated(&user,"Post") // 载入关系字段
    //fmt.Println(user.Post)
    //for _,post := range user.Post{
    //    fmt.Println(post)
    //}


    //=================反向查询====================
    //var posts []models.Post
    //o.QueryTable("post").Filter("user_id", 1).RelatedSel().All(&posts)
    //fmt.Println(posts)







    //=================多对多====================
    //=================多对多====================
    //=================多对多====================
    // Post 和 Tag
    // 新增多对多
    //tag1 := models.Tag{Name: "Python"}
    //tag2 := models.Tag{Name: "编程语言"}
    //o.Insert(&tag1)
    //o.Insert(&tag2)
    //
    //var post models.Post
    //o.QueryTable("post").Filter("id",1).One(&post)
    //m2m := o.QueryM2M(&post,"Tags") // 需要通过m2m 这个对象 才能生成第三张表
    //m2m.Add(&tag1)
    //cnt,_:= m2m.Add(&tag2) // 此时第三张表 中就会有值了
    //fmt.Println(cnt)



    // 多对多查询
    // 任务 查询 post id为1 的 tag

    //=================正向查询====================
    //post := models.Post{Id: 1}
    //o.Read(&post)
    //o.LoadRelated(&post,"Tags") // 载入关系字段
    //fmt.Println(post)
    //for _,tag := range post.Tags{
    //    fmt.Println(tag)
    //}

    //=================反向查询====================
    //var tags []models.Tag
    //o.QueryTable("tag").Filter("posts__post__id",1).All(&tags)
    //fmt.Println(tags)





    //=================QueryM2Mer 多对多操作 ====================
    //=================QueryM2Mer 多对多操作 ====================
    //=================QueryM2Mer 多对多操作 ====================



    //=================创建 QueryM2Mer 对象====================
    //post := models.Post{Id: 1}
    //m2m := o.QueryM2M(&post,"Tags")
    //// 第一个参数的对象,主键必须有值
    //// 第二个参数为对象需要操作的 M2M 字段
    //// QueryM2Mer 的 api 将作用于 Id 为 1 的 Post


    //=================Add====================
    //向M2M关系 tag
    // 给id =2 的post 增加一个 名字为 Golang 的tag
    //post := models.Post{Id: 2}
    //m2m := o.QueryM2M(&post,"Tags")
    //tag := models.Tag{Name: "Golang"}
    //o.Insert(&tag)
    //
    //m2m.Add(&tag)
    // 也可以多个作为参数传入
    // m2m.Add(tag1, tag2, tag3)


    //=================Remove====================
    //从M2M关系中删除 tag
    // 从id 为1 的Post 上去掉 "编程语言" 的tag
    //post := models.Post{Id: 1}
    //m2m := o.QueryM2M(&post,"Tags")
    //
    //var tag models.Tag
    //o.QueryTable("tag").Filter("name","编程语言").One(&tag)
    //
    //m2m.Remove(tag) // 只是从第三张表中 删去记录
    // 也可以多个作为参数传入
    // m2m.Remove(tag1, tag2, tag3)

    //=================Exist====================
    //判断 "编程语言" 这个Tag 是否存在于 post id为1 的 M2M 关系中
    //post := models.Post{Id: 1}
    //m2m := o.QueryM2M(&post,"Tags")
    //
    //var tag models.Tag
    //o.QueryTable("tag").Filter("name","编程语言").One(&tag)
    //ret := m2m.Exist(&tag)
    //fmt.Println(ret)


    //=================Count====================
    // 计算Post id 为1 的tag 的数量
    //post := models.Post{Id: 1}
    //m2m := o.QueryM2M(&post,"Tags")
    //nums,_ := m2m.Count()
    //fmt.Println(nums)

    //=================Clear====================
    // 清除 post id 为1 的 所有的M2M 关系
    //post := models.Post{Id: 1}
    //m2m := o.QueryM2M(&post,"Tags")
    //
    //m2m.Clear()

}
main.go

原生SQL查询: 

https://beego.me/docs/mvc/model/rawsql.md

Beego 模板渲染:

beego 中使用的模板语法,与 go 模板语法基本相同

with end:

with 重定向pipeline :

    <div>{{.person.name}}</div>
    <div>{{.person.age}}</div>
    <div>{{.person.hobby}}</div>
    <hr>
以下方式 和上面的结果是相同的,      
    {{with .person}}
      <div>{{.name}}</div>
        <div>{{.age}}</div>
        <div>{{.hobby}}</div>
    {{end}}
View Code

define :

https://beego.me/docs/mvc/view/tutorial.md#define

define 可以用来定义自模板,可用于模块定义和模板嵌套

注:使用template 调用的时候,后面有个 点 , 因为要把当前位置的上下文传入, 

Beego 中支持直接载入文件模板

{{template "path/to/head.html" .}}
Beego 会依据你设置的模板路径读取 head.html

在模板中可以接着载入其他模板,对于模板的分模块处理很有用处

注:也是需要传入 点的 ,

注释:

允许多行文本注释,不允许嵌套

{{/* comment content
support new line */}}

模板处理:

 

Layout 设计:

pass 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Beego 路由控制:

Beego 控制器函数:

参考: https://beego.me/docs/mvc/controller/controller.md

package controllers

import (
    "fmt"
    "github.com/astaxie/beego"
)

type MainController struct {
    beego.Controller
}

func (c *MainController) Get() {
    c.Data["Website"] = "beego.me"
    c.Data["Email"] = "astaxie@gmail.com"
    c.TplName = "index.tpl"
}

//=====================================
type NestPreparer interface {
    NestPrepare()
}

// baseRouter implemented global settings for all other routers.
type baseController struct {
    beego.Controller
    isLogin bool
}
// Prepare implemented Prepare method for baseRouter.
func (this *baseController) Prepare() {
    fmt.Println("baseController prepare")
    fmt.Println("hello world")

    this.Data["json"]= map[string]interface{}{"name":"tom"}
    this.Finish() // 只会 调 当前的 Finish()
    this.ServeJSON()
    this.StopRun()

    // page start time
    //this.Data["PageStartTime"] = time.Now()

    if app, ok := this.AppController.(NestPreparer); ok {
        app.NestPrepare()
    }
}
func (this *baseController) Finish(){
    fmt.Println("baseController 收尾了 ...")
}

//=====================================
type BaseAdminRouter struct {
    baseController
}
func (this *BaseAdminRouter) Finish(){
    fmt.Println("baseAdminRouter 收尾了 ...")
}

func (this *BaseAdminRouter) NestPrepare() {
    fmt.Println("baseAdminRouter nestPrepare")
}


func (this *BaseAdminRouter) Get(){
    this.Ctx.WriteString("baseAdminRouter get")
}

func (this *BaseAdminRouter) Post(){
    this.Ctx.WriteString("baseAdminRouter post")
}
controllers/default.go

Beego 请求数据处理:

1,获取数据的方式:

  • GetString(key string) string
  • GetStrings(key string) []string
  • GetInt(key string) (int64, error)
  • GetBool(key string) (bool, error)
  • GetFloat(key string) (float64, error)

更多其他的 request 的信息,用户可以通过 this.Ctx.Request 获取信息

2,直接解析到 struct :

如果要把表单里的内容赋值到一个 struct 里,除了用上面的方法一个一个获取再赋值外,beego 提供了通过另外一个更便捷的方式,就是通过 struct 的字段名或 tag 与表单字段对应直接解析到 struct。 

详情:https://beego.me/docs/mvc/controller/params.md#%E7%9B%B4%E6%8E%A5%E8%A7%A3%E6%9E%90%E5%88%B0-struct

关于 struct 的tag :

注意:

  • 定义 struct 时,字段名后如果有 form 这个 tag,则会以把 form 表单里的 name 和 tag 的名称一样的字段赋值给这个字段,否则就会把 form 表单里与字段名一样的表单内容赋值给这个字段。如上面例子中,会把表单中的 username 和 age 分别赋值给 user 里的 Name 和 Age 字段,而 Email 里的内容则会赋给 Email 这个字段。
  • 调用 Controller ParseForm 这个方法的时候,传入的参数必须为一个 struct 的指针,否则对 struct 的赋值不会成功并返回 xx must be a struct pointer 的错误。
  • 如果要忽略一个字段,有两种办法,一是:字段名小写开头二是:form 标签的值设置为 -

3,post 请求时:  获取 Request Body 里的 JSON 或 XML 的数据:

首先:

在配置文件里设置 copyrequestbody = true

type MainController struct {
    beego.Controller
}
func (c *MainController) Post() {
    ret := c.Ctx.Input.RequestBody
    var u User
    json.Unmarshal(ret,&u)
    c.Ctx.WriteString("post ok")
}
controllers/default.go
  <script src="static/js/jquery.js"></script>
  <script>
    $(function () {
      $.ajax({
        url:"http://localhost:8080/",
        type:"post",
        contentType:"application/json",
        data:JSON.stringify({
          "name": "tom",
          "age": 18
        }),
        success:function (res) {
          console.log(res);
        }
      })
    })
  </script>
index.html

4,post 文件上传:

https://beego.me/docs/mvc/controller/params.md#%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0

func (c *MainController) Post() {
    _,h,_ := c.GetFile("upload_file")
    //fmt.Println(f)
    //fmt.Println(h)
    //fmt.Println("===========")
    //fmt.Println(h.Filename)
    //fmt.Println(h.Size) // 字节
    //fmt.Println(h.Header)
    //fmt.Println("===========")
    //fmt.Println(err)

    err := c.SaveToFile("upload_file","static/upload/" + h.Filename)
    fmt.Println(err)

    c.Ctx.WriteString("post ok")
}
controllers/default.go
    <form action="" method="post" enctype="multipart/form-data">
      <input name="upload_file" type="file">
      <input type="submit">
    </form>
index.html

5, 数据绑定:

https://beego.me/docs/mvc/controller/params.md#%E6%95%B0%E6%8D%AE%E7%BB%91%E5%AE%9A

Beego 在表单中使用 PUT 方法:

https://beego.me/docs/mvc/controller/controller.md#%E5%9C%A8%E8%A1%A8%E5%8D%95%E4%B8%AD%E4%BD%BF%E7%94%A8-put-%E6%96%B9%E6%B3%95

跨站请求伪造:

以下内容参考: https://www.cnblogs.com/xiaxiaoxu/p/10428483.html

跨站请求伪造(Cross-site request forgery),简称CSRF 或 XSRF,是Web 应用中常见的一个安全问题。

过程主要是:某用户登录了A网站,认证信息保存在cookie中。当用户访问攻击者创建的B网站时,攻击者通过在B网站发送一个伪造的请求提交到A网站服务器上,让A网站服务器误以为请求来自于自己的网站,于是执行响应的操作,该用户的信息边遭到了篡改。总结起来就是,攻击者利用用户在浏览器中保存的认证信息,向对应的站点发送伪造请求。用户的认证是通过保存在cookie中的数据实现,在发送请求是,只要浏览器中保存了对应的cookie,服务器端就会认为用户已经处于登录状态,而攻击者正是利用了这一机制。

当前防范 XSRF 的一种通用的方法,是对每一个用户都记录一个无法预知的 token 数据,然后要求所有提交的请求(POST/PUT/DELETE)中都必须带有这个 token 数据。如果此数据不匹配 ,那么这个请求就可能是被伪造的。

当处理非GET请求时,要想避免CSRF攻击,关键在判断请求是否来自自己的网站。理论上讲,通过HTTP referrer可以判断原站点从而避免CSRF攻击,但是referer很容易被修改和伪造,所以不能作为主要的防御措施。

除了在表单中加入校验码外,一般的做法是通过在客户端页面中加入伪随机数来防御CSRF攻击,这个伪随机数通过被称为CSRF令牌(token)。

在计算机语境中,令牌(token)指用于标记、验证和传递信息的字符,通常是通过一定算法生成的随机数。

在HTML中,POST方法的请求通过表单创建。我们把在服务器端创建的伪随机数(CSRF令牌)添加到表单中的隐藏字段里和session变量(即签名cookie)中,当用户提交表单时,这个令牌会和表单数据一起提交。在服务器端处理POST请求时,会对表单中的令牌值进行验证,如果表单中的令牌值和seesion中的令牌值相同,就说明请求来自自己的网站。因为CSRF令牌在用户向包含表单的页面发起GET请求时创建,并且在一定时间内过期,一般情况下攻击者无法获取到这个令牌值,所以我们可以有效地区分出请求的来源是否安全

对于AJAX请求,我们可以在XMLHttpRequest请求首部添加一个自定义字段X-CSRFToken来保存CSRF令牌。

如果程序存在XSS漏洞(跨站脚本攻击),那么攻击者可以使用javaScript窃取cookie内容,进而获取CSRF令牌。

beego 中的 XSRF 设置:

https://beego.me/docs/mvc/controller/xsrf.md#%E8%B7%A8%E7%AB%99%E8%AF%B7%E6%B1%82%E4%BC%AA%E9%80%A0

1,在 表单中使用:

https://beego.me/docs/mvc/controller/xsrf.md#%E5%9C%A8%E8%A1%A8%E5%8D%95%E4%B8%AD%E4%BD%BF%E7%94%A8

2,在 ajax 中使用:

https://beego.me/docs/mvc/controller/xsrf.md#%E5%9C%A8-javascript-%E4%B8%AD%E4%BD%BF%E7%94%A8

 
方法一: 通过获取隐藏标签中的 xsrf_token 值,放置在data中 发送, 注 name 是 _xsrf ,
 
  <div id="xsrf_token" style="display: none">{{.xsrf_token}}</div>
  <script src="static/js/jquery.js"></script>
  <script>
    $(function () {
      $.ajax({
        url:"http://localhost:8080/",
        type:"post",
        data:{
          userName :"tom",
          age:18,
          _xsrf:$("#xsrf_token").text(),
        },
        success:res=>{
          console.log(res);
        }
      })
    })


  </script>
index.html
func (c *MainController) Get() {
    c.Data["xsrf_token"] = c.XSRFToken() // 给index.html 页面传入token
    c.TplName = "index.html"
}
controllers/default.go

方法二: 不用隐藏标签:

  <script src="static/js/jquery.js"></script>
  <script>
    $(function () {
      $.ajaxSetup({
        data: {_xsrf: '{{ .xsrf_token }}' },
      });
      $.ajax({
        url:"http://localhost:8080/",
        type:"post",
        data:{
          userName :"tom",
          age:18,
        },
        success:res=>{
          console.log(res);
        }
      })
    })


  </script>
index.html

controllers/default.go 同上,

方法三: 通过放入 请求头中发送,

注意:需要引入一个jquery.cookie.js插件,没用过。

支持controller 级别的屏蔽:

https://beego.me/docs/mvc/controller/xsrf.md#%E6%94%AF%E6%8C%81controller-%E7%BA%A7%E5%88%AB%E7%9A%84%E5%B1%8F%E8%94%BD

XSRF 之前是全局设置的一个参数,如果设置了那么所有的 API 请求都会进行验证,但是有些时候API 逻辑是不需要进行验证的,因此现在支持在controller 级别设置屏蔽

还是很有用的,这个!!! 

beego 中的 session 的控制:

https://beego.me/docs/mvc/controller/session.md

特别注意:

因为 session 内部采用了 gob 来注册存储的对象,例如 struct,所以如果你采用了非 memory 的引擎,请自己在 main.go 的 init 里面注册需要保存的这些结构体,不然会引起应用重启之后出现无法解析的错误,需要用gob.Register(&models.User{}) 注册下!!!

beego 中的过滤器:

https://beego.me/docs/mvc/controller/filter.md

package routers

import (
    "test03/controllers"
    "github.com/astaxie/beego"
)

func init() {
    beego.Router("/", &controllers.MainController{})
    // 过滤器
    beego.InsertFilter("/*", beego.BeforeRouter, controllers.BeforeRouter)  
}
routers/router.go
package controllers

import (
    "fmt"
    "github.com/astaxie/beego/context"
    "reflect"
    )
//=================是否登录过滤器====================
//寻找路由之前
func BeforeRouter(ctx *context.Context)  {
    fmt.Println(ctx.Input.RunController )
    fmt.Println(ctx.Input.RunMethod)

    ctx.Input.RunController = reflect.TypeOf(TestController{})
    ctx.Input.RunMethod = "Test"

    fmt.Println(ctx.Input.RunController )
    fmt.Println(ctx.Input.RunMethod)

}
controllers/custom_filters.go

beego 中的flash 闪存:

 

beego 中的url 构建:

 

beego 中的多种格式输出 json xml jsonp:

什么是 jsonp :

https://www.cnblogs.com/dowinning/archive/2012/04/19/json-jsonp-jquery.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <script src="/static/js/jquery.js"></script>
    <script>
        let script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = 'http://127.0.0.1:8080/?callback=handleCallback';
        document.head.appendChild(script);

        // 回调执行函数
        function handleCallback(res) {
            // alert(JSON.stringify(res));
            console.log(res);
        }
    </script>
</body>
</html>
jsonp 实现跨域请求

常见的跨域解决方案:

https://segmentfault.com/a/1190000011145364

 
jsonp缺点:只能实现get一种请求!!! 
 

beego  表单验证:

https://beego.me/docs/mvc/controller/validation.md

表单验证是用于数据验证和错误收集的模块。

go get github.com/astaxie/beego/validation
 
直接使用:
package main

import (
    "fmt"
    "github.com/astaxie/beego/validation"
)

type User struct {
    Name string
    Age  int
}

func main() {
    u := User{"tomxxxooo", 8}
    valid := validation.Validation{}
    valid.Required(u.Name, "name")
    valid.MaxSize(u.Name, 6, "nameMax")
    valid.Range(u.Age, 0, 18, "age")

    if valid.HasErrors() {
        // 如果有错误信息,证明验证没通过
        // 打印错误信息
        for _, err := range valid.Errors {
            fmt.Println(err.Key,err.Message)
        }
    }

    fmt.Println("========================")
    // or use like this
    v := valid.Max(u.Age,16,"xx_age")
    if !v.Ok{
        fmt.Println(v.Error.Key,v.Error.Message)
    }
    fmt.Println("==================")
    // 定制错误信息
    minAge := 10
    v = valid.Min(u.Age, minAge, "age").Message("少儿不宜o!")
    if !v.Ok{
        fmt.Println(v.Error.Key,v.Error.Message)
    }

    fmt.Println("====================")
    //错误信息格式化
    v = valid.Min(u.Age, minAge, "age").Message("%d不禁", minAge)
    if !v.Ok{
        fmt.Println(v.Error.Key,v.Error.Message)
    }
}
main.go

StructTag 使用:

package main

import (
    "fmt"
    "strings"
    "github.com/astaxie/beego/validation"
)

// 验证函数写在 "valid" tag 的标签里
// 各个函数之间用分号 ";" 分隔,分号后面可以有空格
// 参数用括号 "()" 括起来,多个参数之间用逗号 "," 分开,逗号后面可以有空格
// 正则函数(Match)的匹配模式用两斜杠 "/" 括起来
// 各个函数的结果的 key 值为字段名.验证函数名
type user struct {
    Id     int
    Name   string `valid:"Required;Match(/^Bee.*/)"` // Name 不能为空并且以 Bee 开头
    Age    int    `valid:"Range(1, 140)"`            // 1 <= Age <= 140,超出此范围即为不合法
    Email  string `valid:"Email; MaxSize(100)"`      // Email 字段需要符合邮箱格式,并且最大长度不能大于 100 个字符
    Mobile string `valid:"Mobile"`                   // Mobile 必须为正确的手机号
    IP     string `valid:"IP"`                       // IP 必须为一个正确的 IPv4 地址
}

// 如果你的 struct 实现了接口 validation.ValidFormer
// 当 StructTag 中的测试都成功时,将会执行 Valid 函数进行自定义验证
func (u *user) Valid(v *validation.Validation) {
    if strings.Index(u.Name, "admin") != -1 {
        // 通过 SetError 设置 Name 的错误信息,此时,HasErrors 将会返回 true
        v.SetError("Name", "名称里不能含有 admin")
    }
}

func main() {
    valid := validation.Validation{}
    u := user{Name: "Beegoadmin", Age: 2, Email: "dev@beego.me",Mobile: "17778882661",IP: "120.53.110.1"}
    ok, err := valid.Valid(&u)
    if err != nil {
        fmt.Println(err)
        // handle error
    }
    if !ok {
        for _, err := range valid.Errors {
            fmt.Println(err.Key, err.Message)
        }
    }
}
main.go

beego 错误处理:

主要是404,501 处理,

 

beego 日志处理:

https://beego.me/docs/mvc/controller/logs.md

https://beego.me/docs/module/logs.md

我们的程序往往期望把信息输出到 log 中,现在设置输出到文件很方便,如下所示:beego.SetLogger("file", `{"filename":"logs/test.log"}`)

这个默认情况就会同时输出到两个地方,一个 console,一个 file,如果只想输出到文件,就需要调用删除操作:beego.BeeLogger.DelLogger("console")

 
 
 
 
 
 
 
 
 
 
 
 
 
 
原文地址:https://www.cnblogs.com/zach0812/p/12899783.html