golang学习笔记-8

Golang
396
0
0
2022-04-18

知识点

1、 gorm 迁移

1.1 gorm.DB.AutoMigrate ()

// 自动迁移
db.AutoMigrate(&user.User{},&article.Article{},
)

1.2 字段标签

 //User 用户模型 
type User struct { 
     models.BaseModel 
     Name string `gorm:"column:name;type:varchar(255);not null;unique"` 
     Email string `gorm:"column:email;type:varchar(255);default:NULL;unique;"`
     Password string `gorm:"column:password;type:varchar(255)"` 
 }

上面使用 Struct 元素类型后面跟着 gorm: 开头的就是 GORM 提供的字段标签。告知 GORM 在迁移时,设置数据库字段。

声明 GORM 数据模型时,字段标签是可选的,GORM 支持以下:(注:名大小写不敏感,但建议使用 camelCase 风格)

标签名 说明 column 指定 db 列名 type 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not nullsize, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSINED not NULL AUTO_INSTREMENT size 指定列大小,例如:size:256 primaryKey 指定列为主键 unique 指定列为唯一 default 指定列的默认值 precision 指定列的精度 scale 指定列大小 not null 指定列为 NOT NULL autoIncrement 指定列为自动增长 embedded 嵌套字段 embeddedPrefix 嵌入字段的列名前缀 autoCreateTime 创建时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoCreateTime:nano autoUpdateTime 创建 / 更新时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milli index 根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情 uniqueIndex 与 index 相同,但创建的是唯一索引 check 创建检查约束,例如 check:age > 13,查看 约束 获取详情 <- 设置字段写入的权限, <-:create 只创建、<-:update 只更新、<-:false 无写入权限、<- 创建和更新权限 -> 设置字段读的权限,->:false 无读权限 - 忽略该字段,- 无读写权限

gorm迁移文档: gorm.io/docs/migration.html#conten...

2、 表单验证

比较知名的有 asaskevich/govalidatorthedevsaddam/govalidator

后面基于笔记thedevsaddam/govalidator

2.1 定义表单规则

rules := govalidator.MapData{
    "name":             []
        string{"required", "alpha_num", "between:3,20"},
    "email":            []
        string{"required", "min:4", "max:30", "email"},"password": []
        string{"required", "min:6"},"password_confirm": []
        string{"required"},
}

常用的规则

规则名称 作用 required 字段必须有值 alpha_num 只允许英文字母和数字混合 between:3,20 字段长度介于 3 ~ 20 之间 min:4 最少四个字符 max:30 最大 30 个字符 email 必须为 Email

全部规则:github.com/thedevsaddam/govalidato...

2.2 定义错误信息

// 定制错误消息
    messages := govalidator.MapData{
    "name": []
        string{"required:用户名为必填项","alpha_num:格式错误,只允许数字和英文","between:用户名长度需在 3~20 之间",},
    "email": []
        string{"required:Email 为必填项","min:Email 长度需大于 4","max:Email 长度需小于 30","email:Email 格式不正确,请提供有效的邮箱地址",},"password": []
        string{"required:密码为必填项","min:长度需大于 6",},"password_confirm": []
        string{"required:确认密码框为必填项",},
}

2.3 配置

type User struct { 
    models.BaseModel 
    Name string `gorm:"type:varchar(255);not null;unique" valid:"name"` 
    Email string `gorm:"type:varchar(255);unique;" valid:"email"` 
    Password string `gorm:"type:varchar(255)" valid:"password"` 
    // gorm:"-" —— 设置 GORM 在读写时略过此字段,仅用于表单验证
    PasswordConfirm string `gorm:"-" valid:"password_confirm"` 
}
// 根据 valid 标识的进行对应规则的校验
opts := govalidator.Options{
        Data:          &_user,
        Rules:         rules,
        TagIdentifier: "valid", // Struct 标签标识符 
        Messages:      messages,}

2.4 校验

 // 开始验证
 errs := govalidator.New(opts).ValidateStruct()

2.5 自定义校验规则

govalidator.AddCustomRule()

// 此方法会在初始化时执行
func init() {// not_exists:users,email
    govalidator.AddCustomRule("not_exists", func(field string, rule string, message string, value interface{}) error {
        rng := strings.Split(strings.TrimPrefix(rule, "not_exists:"), ",")

        tableName := rng[0]
        dbFiled := rng[1]
        val := value.(string)

        var count int64
        model.DB.Table(tableName).Where(dbFiled+" = ?", val).Count(&count)

        if count != 0 {

            if message != "" {return errors.New(message)}

            return fmt.Errorf("%v 已被占用", val)}return nil})
}

fmt.Errorf 函数。先调用 fmt.Sprintf 函数,得到确切的错误信息;再调用 errors.New 函数,得到包含该错误信息的 error 类型值,最后返回该值。

3、登录和会话控制

使用 gorilla/sessions 来做会话管理

gorilla/sessions provides cookie and filesystem sessions and infrastructure for custom session backends.
    import ("net/http""github.com/gorilla/sessions")// Note: Don't store your key in your source code. Pass it via an// environmental variable, or flag (or both), and don't accidentally commit it// alongside your code. Ensure your key is sufficiently random - i.e. use Go's// crypto/rand or securecookie.GenerateRandomKey(32) and persist the result.var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))

    func MyHandler(w http.ResponseWriter, r *http.Request) {// Get a session. We're ignoring the error resulted from decoding an// existing session: Get() always returns a session, even if empty.
        session, _ := store.Get(r, "session-name")// Set some session values.
        session.Values["foo"] = "bar"
        session.Values[42] = 43// Save it before we write to the response/return from the handler.
        err := session.Save(r, w)if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)return}}

封装

package session

import ("goblog/pkg/logger""net/http"

    "github.com/gorilla/sessions"
)

// Store gorilla sessions 的存储库
var Store = sessions.NewCookieStore([]byte("33446a9dcf9ea060a0a6532b166da32f304af0de"))

// Session 当前会话
var Session *sessions.Session

// Request 用以获取会话
var Request *http.Request

// Response 用以写入会话
var Response http.ResponseWriter

// StartSession 初始化会话,在中间件中调用
func StartSession(w http.ResponseWriter, r *http.Request) {var err error

    // Store.Get() 的第二个参数是 Cookie 的名称// gorilla/sessions 支持多会话,本项目我们只使用单一会话即可
    Session, err = Store.Get(r, "goblog-session")
    logger.LogError(err)

    Request = r
    Response = w
}

// Put 写入键值对应的会话数据
func Put(key string, value interface{}) {
    Session.Values[key] = value
    Save()
}

// Get 获取会话数据,获取数据时请做类型检测
func Get(key string) interface{} {return Session.Values[key]
}

// Forget 删除某个会话项
func Forget(key string) {delete(Session.Values, key)Save()
}

// Flush 删除当前会话
func Flush() {
    Session.Options.MaxAge = -1Save()
}

// Save 保持会话
func Save() {// 非 HTTPS 的链接无法使用 Secure 和 HttpOnly,浏览器会报错// Session.Options.Secure = true// Session.Options.HttpOnly = true
    err := Session.Save(Request, Response)
    logger.LogError(err)
}

第三方的存储器

例:redis

go get gopkg.in/boj/redistore.v1

Example

// Fetch new store.
store, err := NewRediStore(10, "tcp", ":6379", "", []byte("secret-key"))
if err != nil {panic(err)
}
defer store.Close()

// Get a session.
session, err = store.Get(req, "session-key")
if err != nil {
    log.Error(err.Error())
}

// Add a value.
session.Values["foo"] = "bar"

// Save.
if err = sessions.Save(req, rsp); err != nil {
    t.Fatalf("Error saving session: %v", err)
}

// Delete session.
session.Options.MaxAge = -1
if err = sessions.Save(req, rsp); err != nil {
    t.Fatalf("Error saving session: %v", err)
}

// Change session storage configuration for MaxAge = 10 days.
store.SetMaxAge(10 * 24 * 3600)