package tools import ( "bytes" "encoding/json" "errors" "fmt" "github.com/mitchellh/mapstructure" "io" "net/http" "net/url" "time" "zxsdk/constant" "zxsdk/log" ) 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 }