package http import ( "bytes" "context" "crypto/tls" "encoding/json" "errors" "fmt" "io" "io/ioutil" "mime/multipart" "net/http" "net/url" "strings" "time" ) type Request struct { Method string Url string Host string Timeout time.Duration Header http.Header Client *http.Client Transport *http.Transport requestType RequestType contentType string QueryParams url.Values Body map[string]interface{} bodySize int Result []byte Response *http.Response } func New(opts ...RequestOption) *Request { req := &Request{ Method: GET, Client: &http.Client{ Timeout: 60 * time.Second, Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, DisableKeepAlives: true, Proxy: http.ProxyFromEnvironment, }, }, Transport: nil, bodySize: 10, // default is 10MB Header: make(http.Header), QueryParams: make(url.Values), requestType: TypeJSON, } for _, option := range opts { option(req) } return req } func (p *Request) SetBody(key string, value interface{}) *Request { if p.Body == nil { p.Body = make(map[string]interface{}) } p.Body[key] = value return p } func (p *Request) structureRequest() (io.Reader, error) { if len(p.Method) <= 0 { p.Method = GET } var ( body io.Reader bw *multipart.Writer ) // multipart-form-data if p.requestType == TypeMultipartFormData { body = &bytes.Buffer{} bw = multipart.NewWriter(body.(io.Writer)) } if p.QueryParams != nil && len(p.QueryParams) > 0 { p.Url = fmt.Sprintf("%s?%s", p.Url, p.QueryParams.Encode()) } switch p.Method { case GET: switch p.requestType { case TypeJSON: p.contentType = types[TypeJSON] case TypeForm, TypeFormData, TypeUrlencoded: p.contentType = types[TypeForm] case TypeMultipartFormData: p.contentType = bw.FormDataContentType() case TypeXML: p.contentType = types[TypeXML] default: return body, errors.New("Request type Error ") } case POST, PUT, DELETE, PATCH: switch p.requestType { case TypeJSON: if p.Body != nil { if bodyTmp, err := json.Marshal(p.Body); err != nil { return body, errors.New("marshal error") } else { body = strings.NewReader(string(bodyTmp)) } } p.contentType = types[TypeJSON] case TypeForm, TypeFormData, TypeUrlencoded: body = strings.NewReader(FormatURLParam(p.Body)) p.contentType = types[TypeForm] case TypeMultipartFormData: for k, v := range p.Body { // file 参数 if file, ok := v.(*File); ok { fw, err := bw.CreateFormFile(k, file.Name) if err != nil { return body, err } _, _ = fw.Write(file.Content) continue } // text 参数 vs, ok2 := v.(string) if ok2 { _ = bw.WriteField(k, vs) } else if ss := convertToString(v); ss != "" { _ = bw.WriteField(k, ss) } } _ = bw.Close() p.contentType = bw.FormDataContentType() case TypeXML: body = strings.NewReader(FormatURLParam(p.Body)) p.contentType = types[TypeXML] default: return body, errors.New("Request type Error ") } default: return body, errors.New("Only support GET and POST and PUT and DELETE ") } return body, nil } func (p *Request) Do() error { var ( err error body io.Reader req *http.Request ) if body, err = p.structureRequest(); err != nil { return err } if req, err = http.NewRequestWithContext(context.Background(), p.Method, p.Url, body); err != nil { return err } req.Header = p.Header req.Header.Set("Content-Type", p.contentType) if p.Transport != nil { p.Client.Transport = p.Transport } if p.Host != "" { req.Host = p.Host } if p.Timeout > 0 { p.Client.Timeout = p.Timeout } p.Response, err = p.Client.Do(req) if err != nil { return err } defer func() { _ = p.Response.Body.Close() }() p.Result, err = ioutil.ReadAll(io.LimitReader(p.Response.Body, int64(p.bodySize<<20))) // default 10MB change the size you want if err != nil { return err } return nil }