这是我的第5篇笔记
bug
-
想在判断user是否存在,找不到时返回err,又少写语句
-
DB.Where("name=?", name).Error,无论如何都不会返回err
-
DB.Debug().First(&o, "name=?", "asd").Error,这样才会返回err,当找不到时也会
-
上面方案都不行,改为DB.Model(&UserLogin{}).Where("name=?", name).Count(&c),比较好
- 因为不需要err,也不要不存在时返回err
-
-
绑定query的bug,绑定query应该用form标签,否则不能把query的数据绑定到结构体里
-
用密匙进行token签名时,一定要传入切片
-
所有数字都是float64,除非用反射拿到对应的类型
- 在用jwt4.MapClaims拿token.Claims里面的MapClaims值时,因为这只是map,所以所有数字为float64,如果是结构体就用反射拿到类型
- 所以拿id时注意转为int64
- 不要忘了c.Set(mw.IdentityKey, id),不然可能不太好取值,当然,直接返回也是可以的
-
create_time gorm自动生成,比如2022-06-01 23:48:20
-
gorm设置为Local或者Asia%2fShanghai(%2f是gorm要求的),即中国
-
问题,把时间字符串转为时间戳 出错,多了8小时
-
Parse函数返回UTC,或者在layout和timestr都有时区,返回timestr的时区
- 这很麻烦,
-
ParseInLocation函数则只返回传入的时区,
- 可以是time.Local,系统时间
- 也可以是自定义,如chinaZone, err := time.LoadLocation("Asia/Shanghai")
- 时区文件可以在go的root目录下的 lib\time\zoneinfo.zip 查看
-
-
-
当时把用Parse解析的拿到请求了,所以错了,这个是UTC时间戳,多了8小时
- 现在用ParseInLocation就没问题了
- 教训:解析字符串为时间用Parse函数和ParseInLocation函数要格外注意,其他倒不用注意
-
麻烦事
-
当用jwt中间件时
-
要额外自定义Unauthorized,LoginResponse
-
LoginResponse返回id和token
-
token就是LoginResponse参数message
-
但是id就不好拿了,id可以用设置IdentityKey为"id",这个可以通过ExtractClaims,c.Keys,c.Get()拿取,但是,这3个都是在Keys这个map里
- 而且只有在middlewareImpl调用时,才会 c.Set("JWT_PAYLOAD", claims), c.Set(mw.IdentityKey, identity)
- 而且middlewareImpl是GinJWTMiddleware的handler方法,是要创建GinJWTMiddleware对象后才能通过调用其MiddlewareFunc()方法返回一个函数,这个函数只调用middlewareImpl方法,这是用来做中间件方法--handler的
- 还可以通过GetClaimsFromJWT()拿取,但这个要用GinJWTMiddleware对象调用,在设置LoginResponse时都还没生成呢
- 于是我在Authenticator里用CheckUser返回的userID来调用c.Set(constants.IdentityKey, userID),因为Authenticator是最先调用的,所以在LoginResponse用没问题,当用middlewareImpl时也只是重新设为userID而已
-
-
-
不能从表单拿token,太恶心了,因为推送视频的request的token在表单上,为什么??。。。
-
有一个大大的bug
- id和token的id不对称也会被验证成功,解决方法:使用Authorizator判断
- Authorizator是对user的再次验证,func(data interface{}, c *gin.Context) bool
- data是Identity的value,即id
- 如果request上传了user_id,必须在这里判断这个user_id是否等于Identity
-
Authorizator: func(data interface{}, c *gin.Context) bool { //如果没有userId则不用进行这种隐私校验 //本来,如果没有配置这个函数,也是直接返回true qid := c.Query(constants.UserIdQuery) if qid == "" { return true } queryId, err := strconv.ParseInt(qid, 10, 64) if err != nil { return false } //Authorizator,c.keys["id"]=%!d(float64=1) float64 //这是因为MapClaims只是map,因此从token字符串解析的是float64 id, ok := data.(float64) if !ok { return false } tokenId := int64(id) return tokenId == queryId },
-
领悟
-
事务
- 做多个修改,要么都成功,要么都失败
-
service使用Flow,让Flow这个结构体保存参数,不要一直在函数里传
- 当然,这是要对参数做很多处理(可能由这个参数又参数其他参数),所以保存在Flow里,减少传参,还有方便调用
- 当然,如果对参数做的处理极少,那么可以不要Flow,反而更好
-
查询数据库时,可以选择把对象地址给dao层,这样就不用再返回这个对象了
- 最好还是只在service和dao层之间这么做,不要在handler和service之间也这么做
\