知识点分析
- 网络请求
- 缓存
- 获得token
- 刷新token
- redis的使用
关键代码
定义结构体
关键点
- 能复用的一定要复用
- 能分层的一定要分层
type Config struct { ApiUrl string AppKey string AppSecret string Version string } type Client struct { *Config AccessToken string RefreshToken string } type CommonReq struct { AppKey string `json:"app_key"` Version string `json:"version"` Method string `json:"method"` AccessToken string `json:"access_token"` Timestamp string `json:"timestamp"` Param interface{} `json:"param"` Sign string `json:"sign,omitempty"` } type CommonPageReq struct { PageNo int `json:"pageNo"` PageSize int `json:"pageSize"` } type CommonPageRes struct { PageSize int `json:"pageSize"` PageNo int `json:"pageNo"` TotalPage int `json:"totalPage"` TotalCount int `json:"totalCount"` } type CommonRes struct { Code string `json:"code"` Message string `json:"message"` SubCode string `json:"sub_code"` SubMsg string `json:"sub_msg"` }
签名相关
- 根据三方要求进行签名(验签)
- 比如我这里示例的:md5加密
func (s *Client) sign(req g.Map) string { data := gjson.New(req) _ = data.Remove("sign") return gstr.ToUpper(gmd5.MustEncryptString(server.AppSecret + data.MustToJsonString() + server.AppSecret)) }
封装请求
func (s *Client) post(ctx context.Context, method string, req interface{}) (str string, err error) { Start := gtime.TimestampMilli() allparams := &CommonReq{ AppKey: server.AppKey, Version: server.Version, Method: method, AccessToken: server.AccessToken, Timestamp: gtime.Now().String(), Param: req, } allparams.Sign = s.sign(gconv.Map(allparams)) Request := g.Client() Request.SetHeader("Content-Type", "application/json") resp, err := Request.Timeout(time.Second*5).Post(server.ApiUrl, allparams) defer func() { paramStr := gjson.New(req).MustToJsonString() ctx = context.WithValue(ctx, "Method", "POST") ctx = context.WithValue(ctx, "URI", method) if err != nil { logs.Errorf(ctx, pkgName, logs.FormatErr, paramStr, err.Error(), gtime.TimestampMilli()-Start) } else { logs.Infof(ctx, pkgName, logs.FormatSuc, paramStr, str, gtime.TimestampMilli()-Start) } }() str = resp.ReadAllString() return } func (s *Client) requestApi(ctx context.Context, method string, req interface{}) (str string, err error) { err = s.getAccessToken(ctx) if err != nil { return } str, err = s.post(ctx, method, req) return }
管理token
- 注意看redis的使用
- token一般都是有有效期的,token应该缓存起来,不应该每次都请求获得新的token
- 刷新token一般有两种方式:一种是刷新token的有效期,一种是在有效期内使用旧token获得新token
type accessToken struct { AppKey string `json:"appKey"` AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token"` ExpiresInSeconds int64 `json:"expiresInSeconds"` CreateTime string `json:"createTime"` } //getAccessToken 获取token func (s *Client) getAccessToken(ctx context.Context) (err error) { var token *accessToken cache, _ := g.Redis().DoVar("HGETALL", CacheKey) if !cache.IsEmpty() { _ = cache.Scan(&token) if gtime.NewFromStr(token.CreateTime).Timestamp()+token.ExpiresInSeconds > gtime.Timestamp()-3600 { s.AccessToken = token.AccessToken return } return s.refreshToken(ctx, token) } res, err := Token.Get(ctx) if err != nil { return } if res.Code != "4000" { err = errors.New(res.Message) return } _, err = g.Redis().Do("HMSET", append(g.Slice{CacheKey}, gutil.MapToSlice(gconv.Map(res.Data))...)...) if err != nil { logs.Errorf(ctx, pkgName, logs.FormatErr, err.Error()) } s.AccessToken = res.Data.AccessToken return } //refreshToken 刷新token func (s *Client) refreshToken(ctx context.Context, req *accessToken) (err error) { if gtime.NewFromStr(req.CreateTime).Timestamp()+req.ExpiresInSeconds < gtime.Timestamp() { _, err = g.Redis().DoVar("DEL", CacheKey) return s.getAccessToken(ctx) } res, err := Token.Refresh(ctx, req.RefreshToken) if err != nil { return } if res.Code != "4000" { err = errors.New(res.Message) return } _, _ = g.Redis().Do("HMSET", append(g.Slice{CacheKey}, gutil.MapToSlice(gconv.Map(res.Data))...)...) s.AccessToken = res.Data.AccessToken return }