至信SDK
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

203 lines
6.2 KiB

package tools
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"git.yuanzoo.cn/zhangzhengqiang/popi-zxl/third_party/official/gosdk_demo/zxsdk/constant"
"git.yuanzoo.cn/zhangzhengqiang/popi-zxl/third_party/official/gosdk_demo/zxsdk/log"
"github.com/mitchellh/mapstructure"
"io"
"net/http"
"net/url"
"time"
)
var DefaultHttpClient = &http.Client{
Timeout: 10 * time.Second,
}
// HttpTrigger struct http trigger
type HttpTrigger struct {
Method string
Url string
From string
To string
DisableReqBodyPrint bool
DisableRespBodyPrint bool
HeaderDic map[string]string
QueryDic map[string]string
Client *http.Client
QueryStr string //如果不为空,则忽略QueryDic
}
// SendJsonRequest func sendJsonRequest
func (p *HttpTrigger) SendJsonRequest(client *http.Client, queryStrDic,
headerDic map[string]string,
body interface{}) ([]byte, error) {
return SendRequest(client, queryStrDic, headerDic, p.Method, p.Url, body,
p.From, p.To, p.DisableReqBodyPrint, p.DisableRespBodyPrint, p.QueryStr)
}
// Execute send and get response
func (p *HttpTrigger) Execute(body interface{}) (*constant.RetData, error) {
//不存在则设置默认json格式
if p.HeaderDic == nil {
p.HeaderDic = make(map[string]string)
}
traceId := p.HeaderDic[constant.TraceKey]
if _, ok := p.HeaderDic["Content-Type"]; !ok {
p.HeaderDic["Content-Type"] = "application/json;charset=utf-8"
}
respStr, err := p.SendJsonRequest(p.Client, p.QueryDic, p.HeaderDic, body)
if err != nil {
log.Logger.TraceErrorf(traceId, "SendJsonRequest error:%s", err.Error())
return nil, constant.RESP_HTTP_REQ_ERROR
}
var respData = &constant.RetData{}
err = json.Unmarshal(respStr, respData)
if err != nil {
log.Logger.TraceErrorf(traceId, "Unmarshal error:%s", err.Error())
return nil, constant.JSON_UNMARSHAL_ERROR
}
return respData, nil
}
// HttpGet JSON Get请求,需要服务端的响应体格式符合RetData
// resp必须是结构体的指针,字段是响应Data的内容
// 响应报文JSON的key,需要和respData中的字段名相同(忽略大小写),所以key最好是驼峰命名。否则需要在respData的字段标签中用指定名称,例如`mapstructure:"user_name"`
func (p *HttpTrigger) HttpGet(url string, retData interface{}) error {
p.Method = http.MethodGet
p.Url = url
data, err := p.Execute(nil)
if err != nil {
return err
}
if data.RetCode != constant.RESP_SUCC.RetCode {
err := constant.Build(data.RetCode, data.RetMsg)
return err
}
if retData != nil {
err = mapstructure.Decode(data.Data, retData)
if err != nil {
log.Logger.TraceErrorf(p.HeaderDic[constant.TraceKey], "mapstructure Decode error:%s", err.Error())
return constant.JSON_UNMARSHAL_ERROR
}
}
return nil
}
// HttpPost JSON Post请求,需要服务端的响应体格式符合RetData
// body和resp必须是结构体的指针,resp字段是响应Data的内容
// 响应报文JSON的key,需要和respData中的字段名相同(忽略大小写),
// 所以key最好是驼峰命名。否则需要在respData的字段标签中用指定名称,例如`mapstructure:"user_name"`
func (p *HttpTrigger) HttpPost(url string, body interface{}, retData interface{}) error {
p.Method = http.MethodPost
p.Url = url
data, err := p.Execute(body)
if err != nil {
return err
}
if data.RetCode != constant.RESP_SUCC.RetCode {
err = constant.Build(data.RetCode, data.RetMsg)
return err
}
if retData != nil {
err = mapstructure.Decode(data.Data, retData)
if err != nil {
log.Logger.TraceErrorf(p.HeaderDic[constant.TraceKey], "mapstructure Decode error:%s", err.Error())
return constant.JSON_UNMARSHAL_ERROR
}
}
return nil
}
// SendRequest common func, send request
func SendRequest(client *http.Client, queryStrDic, headerDic map[string]string,
method, reqUrl string, body interface{}, from, to string,
disableReqBodyPrint, disableRespPrint bool, queryStr string) ([]byte, error) {
traceId := headerDic[constant.TraceKey]
// step 1: put query string into url
baseUrl, err := url.Parse(reqUrl)
if err != nil {
log.Logger.TraceErrorf(traceId, "url.Parse err %s", err.Error())
return nil, err
}
if queryStr != "" {
//处理get请求直接透传的场景
baseUrl.RawQuery = queryStr
} else {
params := url.Values{}
for k, v := range queryStrDic {
params.Add(k, v)
}
baseUrl.RawQuery = params.Encode()
}
reqUrl = baseUrl.String()
log.Logger.TraceInfof(traceId, "call %s with reqUrl %s, method %s, from %s", to, reqUrl, method, from)
if !disableReqBodyPrint {
log.Logger.TraceInfof(traceId, "call %s with reqUrl %s, body is %v", to, reqUrl, body)
}
var reader io.Reader
if method != http.MethodGet && body != nil {
if bodyReader, ok := body.(io.Reader); ok {
//处理post请求直接透传的场景
reader = bodyReader
} else {
b, err := json.Marshal(body)
if err != nil {
log.Logger.TraceErrorf(traceId, "json.Marshal err %s", err.Error())
return nil, err
}
log.Logger.TraceInfof(traceId, "post content %s", string(b))
reqBody := bytes.NewBuffer(b)
reader = reqBody
}
}
req, err := http.NewRequest(method, reqUrl, reader)
if err != nil {
log.Logger.TraceErrorf(traceId, "http.NewRequest err %s, reqUrl %s", err.Error(), reqUrl)
return nil, err
}
for k, v := range headerDic {
//Host的请求头要特殊处理,否则设置不到header里去
if k == "Host" {
req.Host = v
} else {
req.Header.Set(k, v)
}
}
beginTime := time.Now()
resp, err := client.Do(req)
if err != nil {
log.Logger.TraceErrorf(traceId, "Client.Do err %s", err.Error())
return nil, err
}
if resp == nil {
errMsg := fmt.Sprintf("resp is nil, may be server can't reach")
log.Logger.TraceErrorf(traceId, errMsg)
return nil, errors.New(errMsg)
}
content, err := io.ReadAll(resp.Body)
if err != nil {
log.Logger.TraceErrorf(traceId, "ioutil.ReadAll err %s", err.Error())
return nil, err
}
log.Logger.TraceInfof(traceId, "call %s with reqUrl %s success and cost %v", to, reqUrl, time.Since(beginTime))
if !disableRespPrint {
log.Logger.TraceInfof(traceId, "call %s with reqUrl %s resp %s", to, reqUrl, string(content))
}
defer resp.Body.Close()
return content, nil
}