Go 原生请求封装

180 min read

Title: Go 请求封装
Category: GOLANG
Tags:

  • markdown

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/url"
	"strings"
)

// Post send post request.
func Post(url, contentType string, args map[string]string) (result string, err error) {
	if contentType == "" {
		contentType = "application/x-www-form-urlencoded"
	}
	resp, err := http.Post(url, contentType, strings.NewReader(argsEncode(args)))
	if err != nil {
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	err = NewError(body)
	if err != nil {
		return
	}
	result = string(body)
	return
}

// VideoDescription 视频描述
type VideoDescription struct {
	Title        string `json:"title"`        // 视频素材的标题
	Introduction string `json:"introduction"` // 视频素材的描述
}

// Upload send post request.
func Upload(uri, filename string, description *VideoDescription, srcFile io.Reader) (result []byte, err error) {
	buf := new(bytes.Buffer)
	// 文件
	writer := multipart.NewWriter(buf)
	formFile, err := writer.CreateFormFile("media", filename)
	if err != nil {
		return nil, err
	}
	if _, err = io.Copy(formFile, srcFile); err != nil {
		return nil, err
	}
	contentType := writer.FormDataContentType()
	// 附加参数
	if description != nil {
		jsonBytes, _ := json.Marshal(description)
		writer.WriteField("description", string(jsonBytes))
	}
	writer.Close() // 发送之前必须调用Close()以写入结尾行

	resp, err := http.Post(uri, contentType, buf)
	if err != nil {
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	err = NewError(body)
	if err != nil {
		return
	}
	result = body
	return
}

// Download 下载非视频文件
func Download(uri string, dis io.Writer) (result []byte, err error) {
	var resp *http.Response
	resp, err = http.Get(uri)
	if err != nil {
		return
	}
	defer resp.Body.Close()
	ctype := resp.Header.Get("Content-Type")
	if strings.Index(strings.ToLower(ctype), "application/json") != -1 {
		result, err = ioutil.ReadAll(resp.Body)
		if err != nil {
			return
		}
		err = NewError(result)
		if err != nil {
			result = nil
		}
	} else {
		io.Copy(dis, resp.Body)
	}
	return
}

// PostJSON send post request.
func PostJSON(url string, jsonObject interface{}) (result []byte, err error) {
	buf := new(bytes.Buffer)
	hjson := json.NewEncoder(buf)
	hjson.SetEscapeHTML(false)
	err = hjson.Encode(jsonObject)
	if err != nil {
		return
	}
	resp, err := http.Post(url, "application/json", buf)
	if err != nil {
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	err = NewError(body)
	if err != nil {
		return
	}
	result = body
	return
}

// Get send get request.
func Get(url string, args map[string]string) (result []byte, err error) {
	if args != nil {
		url += "?" + argsEncode(args)
	}
	resp, err := http.Get(url)
	if err != nil {
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	err = NewError(body)
	if err != nil {
		return
	}
	result = body
	return
}

func argsEncode(params map[string]string) string {
	args := url.Values{}
	for k, v := range params {
		args.Set(k, v)
	}
	return args.Encode()
}

使用

package main

import (
	"context"
	"encoding/json"
	"os"
	"time"

	"github.com/go-redis/redis/v8"
	"github.com/nilorg/go-wechat"
	"github.com/nilorg/pkg/logger"
	"github.com/nilorg/sdk/convert"
	"github.com/nilorg/sdk/signal"
	"github.com/pkg/errors"
)

const (
	envRedisAddrKey        = "REDIS_ADDR"
	envRedisPasswordKey    = "REDIS_PASSWORD"
	envRedisDbKey          = "REDIS_DB"
	envAppIDKey            = "WECHAT_APP_ID"
	envAppSecretKey        = "WECHAT_APP_SECRET"
	envRefreshDurationKey  = "WECHAT_REFRESH_DURATION"
	envRedisAccessTokenKey = "REDIS_ACCESS_TOKEN_KEY"
	envRedisJsAPITicketKey = "REDIS_JS_API_TICKET_KEY"
)

var (
	redisAddr           string = "192.168.*.*:6379"
	redisPassword       string = "Song****"
	redisDb             int    = 1
	appID               string = "wx642f9be216ef3fd6"
	appSecret           string = "ce580a2bcd5de212a1a0d566c8a46a8b"
	refreshDuration     time.Duration = time.Hour
	redisAccessTokenKey               = wechat.RedisAccessTokenKey
	redisJsAPITicketKey               = wechat.RedisJsAPITicketKey
	redisClient         *redis.Client
)

func init() {
	logger.Init()
}

func main() {

	if v := os.Getenv(envRedisAddrKey); v != "" {
		redisAddr = v
	}
	if v := os.Getenv(envRedisPasswordKey); v != "" {
		redisPassword = v
	}
	if v := os.Getenv(envRedisDbKey); v != "" {
		redisDb = convert.ToInt(v)
	}
	if v := os.Getenv(envAppIDKey); v != "" {
		appID = v
	}
	if v := os.Getenv(envAppSecretKey); v != "" {
		appSecret = v
	}
	if v := os.Getenv(envRefreshDurationKey); v != "" {
		refreshDuration = time.Duration(convert.ToInt64(v)) * time.Second
	}
	initRedis()
	logger.Debugln("初始化AccessToken和JsAPITicket")
	refresh()
	ticker := time.NewTicker(refreshDuration)
	go func() { // 异步
		for range ticker.C {
			logger.Debugln("刷新AccessToken和JsAPITicket")
			refresh()
		}
	}()
	signal.AwaitExit()
}

func initRedis() {
	// 初始化Redis
	client := redis.NewClient(&redis.Options{
		Addr:     redisAddr,
		Password: redisPassword,
		DB:       redisDb,
	})
	_, err := client.Ping(context.Background()).Result()
	if err != nil {
		logger.Fatalf(
			"Init redis connection failed: %s ",
			errors.Wrap(err, "Ping redis failed"),
		)
	}
	redisClient = client
}

func refresh() {
	token := refreshAccessToken() // 刷新AccessToken
	if token != "" {
		refreshJsAPITicket(token) // 刷新JsAPITicket
	}
}

// refreshAccessToken ...
// https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183
func refreshAccessToken() string {
	result, err := wechat.Get("https://api.weixin.qq.com/cgi-bin/token", map[string]string{
		"appid":      appID,
		"secret":     appSecret,
		"grant_type": "client_credential",
	})
	if err != nil {
		logger.Debugf("刷新AccessToken错误:%v", err)
		return ""
	}
	reply := new(wechat.AccessTokenReply)
	json.Unmarshal(result, reply)

	if err := redisClient.Set(context.Background(), redisAccessTokenKey, reply.AccessToken, time.Second*time.Duration(reply.ExpiresIn)).Err(); err != nil {
		logger.Errorf("redisClient.Set %s Value: %s Error: %s", redisAccessTokenKey, reply.AccessToken, err)
	}
	logger.Debugf("最新AccessToken: %s", reply.AccessToken)
	return reply.AccessToken
}

// refreshJsAPITicket ...
// https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115
func refreshJsAPITicket(token string) {
	result, err := wechat.Get("https://api.weixin.qq.com/cgi-bin/ticket/getticket", map[string]string{
		"access_token": token,
		"type":         "jsapi",
	})
	if err != nil {
		logger.Debugf("刷新Ticket错误:%v", err)
		return
	}
	reply := new(wechat.JsAPITicketReply)
	json.Unmarshal(result, reply)
	logger.Debugf("最新JsAPITicket: %s", reply.Ticket)
	if err := redisClient.Set(context.Background(), redisJsAPITicketKey, reply.Ticket, time.Second*time.Duration(reply.ExpiresIn)).Err(); err != nil {
		logger.Errorf("redisClient.Set %s Value: %s Error: %s", redisJsAPITicketKey, reply.Ticket, err)
	}
}