|
- package http
-
- import (
- "fmt"
- "io"
- "mime/multipart"
- "net/http"
- ul "net/url"
- "strings"
-
- "gitlink.org.cn/cloudream/common/pkgs/iterator"
- "gitlink.org.cn/cloudream/common/utils/serder"
- )
-
- const (
- ContentTypeJSON = "application/json"
- ContentTypeForm = "application/x-www-form-urlencoded"
- ContentTypeMultiPart = "multipart/form-data"
- ContentTypeOctetStream = "application/octet-stream"
- )
-
- type RequestParam struct {
- Header any
- Query any
- Body any
- }
-
- func GetJSON(url string, param RequestParam) (*http.Response, error) {
- req, err := http.NewRequest(http.MethodGet, url, nil)
- if err != nil {
- return nil, err
- }
-
- if err = prepareQuery(req, param.Query); err != nil {
- return nil, err
- }
-
- if err = prepareHeader(req, param.Header); err != nil {
- return nil, err
- }
-
- if err = prepareJSONBody(req, param.Body); err != nil {
- return nil, err
- }
-
- return http.DefaultClient.Do(req)
- }
-
- func GetForm(url string, param RequestParam) (*http.Response, error) {
- req, err := http.NewRequest(http.MethodGet, url, nil)
- if err != nil {
- return nil, err
- }
-
- if err = prepareQuery(req, param.Query); err != nil {
- return nil, err
- }
-
- if err = prepareHeader(req, param.Header); err != nil {
- return nil, err
- }
-
- if err = prepareFormBody(req, param.Body); err != nil {
- return nil, err
- }
-
- return http.DefaultClient.Do(req)
- }
-
- func PostJSON(url string, param RequestParam) (*http.Response, error) {
- req, err := http.NewRequest(http.MethodPost, url, nil)
- if err != nil {
- return nil, err
- }
-
- if err = prepareQuery(req, param.Query); err != nil {
- return nil, err
- }
-
- if err = prepareHeader(req, param.Header); err != nil {
- return nil, err
- }
-
- if err = prepareJSONBody(req, param.Body); err != nil {
- return nil, err
- }
-
- return http.DefaultClient.Do(req)
- }
-
- func PostForm(url string, param RequestParam) (*http.Response, error) {
- req, err := http.NewRequest(http.MethodPost, url, nil)
- if err != nil {
- return nil, err
- }
-
- if err = prepareQuery(req, param.Query); err != nil {
- return nil, err
- }
-
- if err = prepareHeader(req, param.Header); err != nil {
- return nil, err
- }
-
- if err = prepareFormBody(req, param.Body); err != nil {
- return nil, err
- }
-
- return http.DefaultClient.Do(req)
- }
-
- func ParseJSONResponse[TBody any](resp *http.Response) (TBody, error) {
- var ret TBody
- contType := resp.Header.Get("Content-Type")
- if strings.Contains(contType, ContentTypeJSON) {
- if err := serder.JSONToObjectStream(resp.Body, &ret); err != nil {
- return ret, fmt.Errorf("parsing response: %w", err)
- }
-
- return ret, nil
- }
-
- return ret, fmt.Errorf("unknow response content type: %s, status: %d", contType, resp.StatusCode)
- }
-
- type MultiPartRequestParam struct {
- Header any
- Query any
- Form any
- Files MultiPartFileIterator
- }
-
- type MultiPartFileIterator = iterator.Iterator[*IterMultiPartFile]
- type IterMultiPartFile struct {
- FieldName string // 这个文件所属的form字段
- FileName string // 文件名
- File io.ReadCloser
- }
-
- func PostMultiPart(url string, param MultiPartRequestParam) (*http.Response, error) {
- req, err := http.NewRequest(http.MethodPost, url, nil)
- if err != nil {
- return nil, err
- }
-
- if err = prepareQuery(req, param.Query); err != nil {
- return nil, err
- }
-
- if err = prepareHeader(req, param.Header); err != nil {
- return nil, err
- }
-
- pr, pw := io.Pipe()
- muWriter := multipart.NewWriter(pw)
-
- setHeader(req.Header, "Content-Type", fmt.Sprintf("%s;boundary=%s", ContentTypeMultiPart, muWriter.Boundary()))
-
- writeResult := make(chan error, 1)
- go func() {
- writeResult <- func() error {
- defer pw.Close()
- defer muWriter.Close()
-
- if param.Form != nil {
- mp, err := serder.ObjectToMap(param.Form)
- if err != nil {
- return fmt.Errorf("formValues object to map failed, err: %w", err)
- }
-
- for k, v := range mp {
- err := muWriter.WriteField(k, fmt.Sprintf("%v", v))
- if err != nil {
- return fmt.Errorf("write form field failed, err: %w", err)
- }
- }
- }
-
- for {
- file, err := param.Files.MoveNext()
- if err == iterator.ErrNoMoreItem {
- break
- }
- if err != nil {
- return fmt.Errorf("opening file: %w", err)
- }
-
- err = func() error {
- defer file.File.Close()
-
- w, err := muWriter.CreateFormFile(file.FieldName, file.FileName)
- if err != nil {
- return fmt.Errorf("create form file failed, err: %w", err)
- }
-
- _, err = io.Copy(w, file.File)
- if err != nil {
- return err
- }
- return nil
- }()
- if err != nil {
- return err
- }
- }
-
- return nil
- }()
- }()
-
- req.Body = pr
-
- cli := http.Client{}
- resp, err := cli.Do(req)
- if err != nil {
- return nil, err
- }
-
- writeErr := <-writeResult
- if writeErr != nil {
- return nil, writeErr
- }
-
- return resp, nil
- }
-
- func prepareQuery(req *http.Request, query any) error {
- if query == nil {
- return nil
- }
-
- mp, ok := query.(map[string]any)
- if !ok {
- var err error
- if mp, err = serder.ObjectToMap(query); err != nil {
- return fmt.Errorf("query object to map: %w", err)
- }
- }
-
- values := make(ul.Values)
- for k, v := range mp {
- values.Add(k, fmt.Sprintf("%v", v))
- }
-
- req.URL.RawQuery = values.Encode()
- return nil
- }
-
- func prepareHeader(req *http.Request, header any) error {
- if header == nil {
- return nil
- }
-
- mp, ok := header.(map[string]any)
- if !ok {
- var err error
- if mp, err = serder.ObjectToMap(header); err != nil {
- return fmt.Errorf("header object to map: %w", err)
- }
- }
-
- req.Header = make(http.Header)
- for k, v := range mp {
- req.Header.Set(k, fmt.Sprintf("%v", v))
- }
- return nil
- }
-
- func prepareJSONBody(req *http.Request, body any) error {
- setHeader(req.Header, "Content-Type", ContentTypeJSON)
-
- if body == nil {
- return nil
- }
-
- req.Body = serder.ObjectToJSONStream(body)
- return nil
- }
-
- func prepareFormBody(req *http.Request, body any) error {
- setHeader(req.Header, "Content-Type", ContentTypeForm)
-
- if body == nil {
- return nil
- }
-
- mp, ok := body.(map[string]any)
- if !ok {
- var err error
- if mp, err = serder.ObjectToMap(body); err != nil {
- return fmt.Errorf("body object to map: %w", err)
- }
- }
-
- values := make(ul.Values)
- for k, v := range mp {
- values.Add(k, fmt.Sprintf("%v", v))
- }
-
- req.Body = io.NopCloser(strings.NewReader(values.Encode()))
- return nil
- }
-
- func setHeader(mp http.Header, key, value string) http.Header {
- if mp == nil {
- mp = make(http.Header)
- }
-
- mp.Set(key, value)
- return mp
- }
-
- func setValue(values ul.Values, key, value string) ul.Values {
- if values == nil {
- values = make(ul.Values)
- }
-
- values.Add(key, value)
- return values
- }
|