123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- package request
- import (
- "bytes"
- "context"
- "encoding/json"
- "encoding/xml"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "os"
- "path"
- "reflect"
- "regexp"
- "strings"
- )
- const (
- JSON = "application/json"
- XML = "application/xml"
- plainTextType = "text/plain; charset=utf-8"
- jsonContentType = "application/json"
- formContentType = "application/x-www-form-urlencoded"
- )
- var (
- jsonCheck = regexp.MustCompile(`(?i:(application|text)/(json|.*\+json|json\-.*)(;|$))`)
- xmlCheck = regexp.MustCompile(`(?i:(application|text)/(xml|.*\+xml)(;|$))`)
- )
- type Request struct {
- context context.Context
- method string
- uri string
- url *url.URL
- body any
- query url.Values
- formData url.Values
- header http.Header
- contentType string
- authorization Authorization
- client *Client
- rawRequest *http.Request
- rawResponse *http.Response
- }
- func (r *Request) detectContentType(body interface{}) string {
- contentType := plainTextType
- kind := reflect.Indirect(reflect.ValueOf(body)).Type().Kind()
- switch kind {
- case reflect.Struct, reflect.Map:
- contentType = jsonContentType
- case reflect.String:
- contentType = plainTextType
- default:
- if b, ok := body.([]byte); ok {
- contentType = http.DetectContentType(b)
- } else if kind == reflect.Slice {
- contentType = jsonContentType
- }
- }
- return contentType
- }
- func (r *Request) readRequestBody(contentType string, body any) (reader io.Reader, err error) {
- var (
- ok bool
- s string
- buf []byte
- )
- kind := reflect.Indirect(reflect.ValueOf(body)).Type().Kind()
- if reader, ok = r.body.(io.Reader); ok {
- return reader, nil
- }
- if buf, ok = r.body.([]byte); ok {
- goto __end
- }
- if s, ok = r.body.(string); ok {
- buf = []byte(s)
- goto __end
- }
- if jsonCheck.MatchString(contentType) && (kind == reflect.Struct || kind == reflect.Map || kind == reflect.Slice) {
- buf, err = json.Marshal(r.body)
- goto __end
- }
- if xmlCheck.MatchString(contentType) && (kind == reflect.Struct) {
- buf, err = xml.Marshal(r.body)
- goto __end
- }
- err = fmt.Errorf("unmarshal content type %s", contentType)
- __end:
- if err == nil {
- if len(buf) > 0 {
- return bytes.NewReader(buf), nil
- }
- }
- return
- }
- func (r *Request) SetContext(ctx context.Context) *Request {
- r.context = ctx
- return r
- }
- func (r *Request) AddQuery(k, v string) *Request {
- r.query.Add(k, v)
- return r
- }
- func (r *Request) SetQuery(vs map[string]string) *Request {
- for k, v := range vs {
- r.query.Set(k, v)
- }
- return r
- }
- func (r *Request) AddFormData(k, v string) *Request {
- r.contentType = formContentType
- r.formData.Add(k, v)
- return r
- }
- func (r *Request) SetFormData(vs map[string]string) *Request {
- r.contentType = formContentType
- for k, v := range vs {
- r.formData.Set(k, v)
- }
- return r
- }
- func (r *Request) SetBody(v any) *Request {
- r.body = v
- return r
- }
- func (r *Request) SetContentType(v string) *Request {
- r.contentType = v
- return r
- }
- func (r *Request) AddHeader(k, v string) *Request {
- r.header.Add(k, v)
- return r
- }
- func (r *Request) SetHeader(h http.Header) *Request {
- r.header = h
- return r
- }
- func (r *Request) Do() (res *http.Response, err error) {
- var s string
- s = r.formData.Encode()
- if len(s) > 0 {
- r.body = s
- }
- r.url.RawQuery = r.query.Encode()
- r.uri = r.url.String()
- return r.client.execute(r)
- }
- func (r *Request) Response(v any) (err error) {
- var (
- res *http.Response
- buf []byte
- contentType string
- )
- if res, err = r.Do(); err != nil {
- return
- }
- defer func() {
- _ = res.Body.Close()
- }()
- if res.StatusCode/100 != 2 {
- if buf, err = io.ReadAll(res.Body); err == nil && len(buf) > 0 {
- err = fmt.Errorf("http response %s(%d): %s", res.Status, res.StatusCode, string(buf))
- } else {
- err = fmt.Errorf("http response %d: %s", res.StatusCode, res.Status)
- }
- return
- }
- contentType = strings.ToLower(res.Header.Get("Content-Type"))
- extName := path.Ext(r.rawRequest.URL.String())
- if strings.Contains(contentType, JSON) || extName == ".json" {
- err = json.NewDecoder(res.Body).Decode(v)
- } else if strings.Contains(contentType, XML) || extName == ".xml" {
- err = xml.NewDecoder(res.Body).Decode(v)
- } else {
- err = fmt.Errorf("unsupported content type: %s", contentType)
- }
- return
- }
- func (r *Request) Download(s string) (err error) {
- var (
- fp *os.File
- res *http.Response
- )
- if res, err = r.Do(); err != nil {
- return
- }
- defer func() {
- _ = res.Body.Close()
- }()
- if fp, err = os.OpenFile(s, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644); err != nil {
- return
- }
- defer func() {
- _ = fp.Close()
- }()
- _, err = io.Copy(fp, res.Body)
- return
- }
- func newRequest(method string, uri string, client *Client) *Request {
- var (
- err error
- )
- r := &Request{
- context: context.Background(),
- method: method,
- uri: uri,
- header: make(http.Header),
- formData: make(url.Values),
- client: client,
- }
- if r.url, err = url.Parse(uri); err == nil {
- r.query = r.url.Query()
- } else {
- r.query = make(url.Values)
- }
- return r
- }
|