推荐用 int64 字段接收时间戳并手动校验:expiredat int64 json:"expired_at" binding:"required",在 bind 后用 if req.expiredat
如何用 Gin 的
binding解析并校验时间戳字段Gin 默认的
time.Time绑定不直接支持 Unix 时间戳(int64),若结构体字段声明为time.Time却传入数字型时间戳,会报json: cannot unmarshal number into Go struct field错误。必须显式指定标签或自定义绑定逻辑。推荐做法是:字段保持
int64类型,配合自定义校验函数判断是否过期。这样既避免反序列化失败,又便于做业务语义判断(比如“过期 = 小于当前时间”)。
- 结构体中用
int64接收时间戳,例如:ExpiredAt int64 `json:"expired_at" binding:"required"`- 校验逻辑放在
Bind之后、业务处理之前,不要依赖binding内置的时间比较规则(它只认字符串格式的 RFC3339/ISO8601)- 若坚持用
time.Time字段,需实现自定义UnmarshalJSON方法,但增加维护成本,一般不必要Gin 中校验时间戳是否过期的通用写法
核心逻辑很简单:获取当前 Unix 时间戳,与字段值比较。但要注意时区和精度——所有 Unix 时间戳默认是 UTC 秒级,
time.Now().Unix()返回的就是秒级整数,可直接比对。
Golang Spf13 Viper下载Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
示例代码片段:
type TokenReq struct { ExpiredAt int64 `json:"expired_at" binding:"required"` } func handleToken(c *gin.Context) { var req TokenReq if err := c.ShouldBindJSON(&req); err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } now := time.Now().Unix() if req.ExpiredAt
- 用
time.Now().Unix()而非time.Now().UnixMilli(),除非你明确接收毫秒级时间戳且字段是int64毫秒值- 前端传参务必统一单位(秒 or 毫秒),后端不做自动转换,避免歧义
- 不要在
binding标签里写类似gt=1717027200这种硬编码时间,校验逻辑应动态计算为什么不用
validator的gtfield或datetimeGin 默认用 go-playground/validator 做结构体校验,但它对时间戳的支持很弱:
datetime只校验字符串格式;gtfield要求两个字段都是数值且同类型,但无法把time.Now().Unix()注入为字段参与比较。
gtfield是字段间比较,不是字段与运行时值比较- 想用 validator 实现动态过期校验,得写自定义函数并注册到验证器,反而比手动判断更重
- 简单场景下,手动
if req.ExpiredAt 更直观、易测、无隐式依赖注意时区和测试可重复性
线上环境通常设为 UTC,但本地开发机可能是 CST 或其他时区。虽然
time.Now().Unix()总是返回 UTC 秒数,不受本地时区影响,但写单元测试时容易出错——比如用固定时间戳构造请求,却忘了测试时的time.Now()是实时的。实际项目里,时间戳过期校验往往只是整个鉴权链的一环,真正容易被忽略的是:**没确认前端传的是秒还是毫秒,也没在 API 文档里写清楚**。一旦前后端单位不一致,错误会静默发生——比如 token 明明刚生成就提示过期。
- 测试时建议用
gomonkey打桩time.Now,或把时间获取逻辑抽成函数并注入- 避免在 handler 里直接调用
time.Now()多次,可能因纳秒级差异导致边界 case 不稳定- 如果过期时间允许“刚好等于现在”也算有效,用
;若必须严格大于,用 <code> ——这个语义要和前端对齐
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!












