zoukankan      html  css  js  c++  java
  • 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")

     
     
     
     
     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    Whidbey 开发
    构建软件数字安全带系列课程
    微软产品生命周期模型系列课程
    Windows Mobile 2nd 开发黄金周
    微软高性能运算系列课程
    _tmain()和main()区别
    简单工厂模式&工厂方法模式
    单例模式
    java中去除字符串(String)中的换行字符(\r \n \t)
    阿里巴巴 Java开发手册1.4.0
  • 原文地址:https://www.cnblogs.com/zach0812/p/12899783.html
Copyright © 2011-2022 走看看