一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情。
前言
本月会持续更新Go语言相关的文章,尤其是GoFrame,感兴趣的同学可以关注我,结伴而行。
同时会沉淀总结一下:《中台开发实践》、《私有化部署实践》、《深入理解goroutine及使用实践》、《如何在开发过程中把GO语言的价值体现出来》。
立志沉淀一些质量高的内容出来。
今天这篇分享:使用Go语言对接三方API实践。
知识点分析
- 网络请求
- 缓存
- 获得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
}
总结
这篇总结了比较典型的使用Go语言对接三方API请求token、刷新token、封装请求、使用Redis缓存token的实践。
整体思路比较清晰,尤其是Redis的使用部分,使用了GoFrame框架的常用函数。
对GO感兴趣的朋友可以查看我之前写的文章,了解一下Go的魅力:
欢迎大家关注我的Go语言学习专栏
,我会持续更新在Go学习和使用过程中的干货分享。
最后
感谢阅读,欢迎大家三连:点赞、收藏、投币(关注)!!!