package util import ( "encoding/json" "errors" "fmt" "io" "net/http" netUrl "net/url" "strings" "time" "github.com/zeromicro/go-zero/core/logx" ) const ( TimeOut = time.Duration(30) * time.Second // http请求超时时间, 默认30s ) type HttpUtil struct { log logx.Logger ReqConfig *ReqConfig // 请求参数 TimeOut time.Duration // 超时时间 Request *http.Request // 请求request Response *Response // 响应结果 Error error } // ReqConfig 请求参数 type ReqConfig struct { Headers map[string]string QueryArgs map[string]any BodyArgs map[string]any } type Response struct { Status string // e.g. "200 OK" StatusCode int // e.g. 200 Header http.Header Body []byte // body实体,即io.ReadAll(resp.body)后的结果 } func NewHttpUtil(log logx.Logger) *HttpUtil { return &HttpUtil{ log: log, } } // Get GET请求 // @param url 请求的http地址 // @param reqConfig 中header、query、body等参数 func (h *HttpUtil) Get(url string, reqConfig *ReqConfig) *HttpUtil { return h.NewRequest(http.MethodGet, url, reqConfig) } // Post POST请求 func (h *HttpUtil) Post(url string, reqConfig *ReqConfig) *HttpUtil { return h.NewRequest(http.MethodPost, url, reqConfig) } // Put PUT请求 func (h *HttpUtil) Put(url string, reqConfig *ReqConfig) *HttpUtil { return h.NewRequest(http.MethodPut, url, reqConfig) } // Delete DELETE请求 func (h *HttpUtil) Delete(url string, reqConfig *ReqConfig) *HttpUtil { return h.NewRequest(http.MethodDelete, url, reqConfig) } // NewRequest 组装请求参数 func (h *HttpUtil) NewRequest(method, url string, reqConfig *ReqConfig) (hc *HttpUtil) { if h.Error != nil { return h } h.ReqConfig = reqConfig hc = h // 请求 req, err := http.NewRequest(method, url, nil) if err != nil { h.log.WithFields([]logx.LogField{{Key: "method", Value: method}, {Key: "url", Value: url}, {Key: "reqConfig", Value: reqConfig}}...). Errorf("[HttpUtil][NewRequest] http new request error", err) hc.Error = err return } hc.Request = req // 请求参数 err = hc.requestConfig() if err != nil { hc.Error = err return } return } // Do http请求及响应处理 func (h *HttpUtil) Do() (hc *HttpUtil) { if h.Error != nil { return h } hc = h // http请求 timeOut := hc.TimeOut if hc.TimeOut == 0 { timeOut = TimeOut } client := &http.Client{} client.Timeout = timeOut // 开始请求 resp, err := client.Do(hc.Request) if err != nil { h.log.WithFields(logx.LogField{Key: "req", Value: hc.Request}). Errorf("[HttpUtil][Do] http client do error", err) hc.Error = err return } defer resp.Body.Close() content, err := io.ReadAll(resp.Body) if err != nil { h.log.WithFields(logx.LogField{Key: "req", Value: hc.Request}). Errorf("[HttpUtil][Do] http response ReadAll error", err) hc.Error = err return } fmt.Printf("===== http response content: %s\n", content) hc.Response = &Response{ Status: resp.Status, StatusCode: resp.StatusCode, Header: resp.Header, Body: content, } return } // Result 获取http的响应结果 // @param result 为结构体(指针类型),解析http请求返回数据json.Unmarshal后的结构 func (h *HttpUtil) Result(result any) (hc *HttpUtil) { if h.Error != nil { return h } hc = h if result == nil { return } if hc.Response == nil || len(hc.Response.Body) == 0 { return } err := json.Unmarshal(hc.Response.Body, &result) if err != nil { h.log.WithFields(logx.LogField{Key: "HttpUtil", Value: hc}, logx.LogField{Key: "result", Value: result}). Errorf("[HttpUtil][Result] http response body json unmarshal error", err) hc.Error = err return } return } // 请求参数 func (h *HttpUtil) requestConfig() error { if h.Request == nil { return errors.New("http request is nil") } if h.ReqConfig == nil { return nil } // header参数 if h.ReqConfig.Headers != nil { for key, value := range h.ReqConfig.Headers { h.Request.Header.Set(key, value) } } // 请求query参数 queryParams := h.Request.URL.Query() if h.ReqConfig.QueryArgs != nil { for key, value := range h.ReqConfig.QueryArgs { queryParams.Add(key, fmt.Sprint(value)) } } h.Request.URL.RawQuery = queryParams.Encode() // 请求body参数 if h.ReqConfig.BodyArgs != nil { switch h.Request.Header.Get("Content-Type") { case "application/x-www-form-urlencoded": form := make(netUrl.Values) for k, v := range h.ReqConfig.BodyArgs { form.Add(k, fmt.Sprint(v)) } h.Request.Body = io.NopCloser(strings.NewReader(form.Encode())) case "application/json": data, err := json.Marshal(h.ReqConfig.BodyArgs) if err != nil { h.Error = err return err } h.Request.Body = io.NopCloser(strings.NewReader(string(data))) } } return nil }