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.

192 lines
4.0 KiB

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
}