Go对接三方API实践

2,415 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

前言

本月会持续更新Go语言相关的文章,尤其是GoFrame,感兴趣的同学可以关注我,结伴而行。

同时会沉淀总结一下:《中台开发实践》、《私有化部署实践》、《深入理解goroutine及使用实践》、《如何在开发过程中把GO语言的价值体现出来》。

立志沉淀一些质量高的内容出来。

今天这篇分享:使用Go语言对接三方API实践。

知识点分析

  1. 网络请求
  2. 缓存
  3. 获得token
  4. 刷新token
  5. redis的使用

关键代码

定义结构体

关键点

  1. 能复用的一定要复用
  2. 能分层的一定要分层
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"`
}

签名相关

  1. 根据三方要求进行签名(验签)
  2. 比如我这里示例的: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

  1. 注意看redis的使用
  2. token一般都是有有效期的,token应该缓存起来,不应该每次都请求获得新的token
  3. 刷新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语言为什么值得学习?

我的PHP转Go之旅

回顾一下我的Go学习之旅

非常适合PHP和Java转Go学习的框架:GoFrame

欢迎大家关注我的Go语言学习专栏,我会持续更新在Go学习和使用过程中的干货分享。

Go语言学习专栏

最后

感谢阅读,欢迎大家三连:点赞、收藏、投币(关注)!!!

8e95dac1fd0b2b1ff51c08757667c47a.gif