You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

sdks.go 5.9 kB

4 months ago
4 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. package sdks
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "mime"
  7. "mime/multipart"
  8. "net/http"
  9. "net/url"
  10. "strings"
  11. "github.com/google/go-querystring/query"
  12. "gitlink.org.cn/cloudream/common/consts/errorcode"
  13. "gitlink.org.cn/cloudream/common/utils/http2"
  14. "gitlink.org.cn/cloudream/common/utils/io2"
  15. "gitlink.org.cn/cloudream/common/utils/serder"
  16. )
  17. type CodeMessageError struct {
  18. Code string
  19. Message string
  20. }
  21. func (e *CodeMessageError) Error() string {
  22. return fmt.Sprintf("code: %s, message: %s", e.Code, e.Message)
  23. }
  24. type RequestParam struct {
  25. Method string
  26. Path string
  27. Query url.Values
  28. Header http.Header
  29. Body RequestBody
  30. }
  31. func (p *RequestParam) MakeRequest(baseURL string) (*http.Request, error) {
  32. var body io.ReadCloser
  33. bodyLen := int64(0)
  34. if p.Body != nil {
  35. body = p.Body.IntoStream()
  36. bodyLen = p.Body.Length()
  37. }
  38. req, err := http.NewRequest(p.Method, joinUrlUnsafe(baseURL, p.Path), body)
  39. if err != nil {
  40. return nil, err
  41. }
  42. req.ContentLength = bodyLen
  43. req.URL.RawQuery = p.Query.Encode()
  44. if p.Header != nil {
  45. req.Header = p.Header
  46. }
  47. return req, nil
  48. }
  49. func (p *RequestParam) Do(baseURL string, cli *http.Client) (*http.Response, error) {
  50. req, err := p.MakeRequest(baseURL)
  51. if err != nil {
  52. return nil, err
  53. }
  54. return cli.Do(req)
  55. }
  56. func joinUrlUnsafe(base string, path string) string {
  57. if strings.HasSuffix(base, "/") {
  58. if strings.HasPrefix(path, "/") {
  59. return base + path[1:]
  60. }
  61. return base + path
  62. }
  63. if strings.HasPrefix(path, "/") {
  64. return base + path
  65. }
  66. return base + "/" + path
  67. }
  68. type RequestBody interface {
  69. // 请求体长度,如果长度未知,返回-1
  70. Length() int64
  71. // 将内部值变成一个流,用于发送请求
  72. IntoStream() io.ReadCloser
  73. }
  74. type StringBody struct {
  75. Value string
  76. }
  77. func (s *StringBody) Length() int64 {
  78. return int64(len(s.Value))
  79. }
  80. func (s *StringBody) IntoStream() io.ReadCloser {
  81. return io.NopCloser(bytes.NewReader([]byte(s.Value)))
  82. }
  83. type BytesBody struct {
  84. Value []byte
  85. }
  86. func (b *BytesBody) Length() int64 {
  87. return int64(len(b.Value))
  88. }
  89. func (b *BytesBody) IntoStream() io.ReadCloser {
  90. return io.NopCloser(bytes.NewReader(b.Value))
  91. }
  92. type StreamBody struct {
  93. Stream io.ReadCloser
  94. LengthHint int64 // 长度提示,如果长度未知,可以设置为-1
  95. }
  96. func (s *StreamBody) Length() int64 {
  97. return s.LengthHint
  98. }
  99. func (s *StreamBody) IntoStream() io.ReadCloser {
  100. return s.Stream
  101. }
  102. type APIRequest interface {
  103. MakeParam() *RequestParam
  104. }
  105. func MakeJSONParam(method string, path string, body any) *RequestParam {
  106. data, err := serder.ObjectToJSONEx(body)
  107. if err != nil {
  108. // 开发人员应该保证param是可序列化的
  109. panic(err)
  110. }
  111. return &RequestParam{
  112. Method: method,
  113. Path: path,
  114. Body: &BytesBody{Value: data},
  115. }
  116. }
  117. func MakeQueryParam(method string, path string, q any) *RequestParam {
  118. values, err := query.Values(q)
  119. if err != nil {
  120. // 开发人员应该保证param是可序列化的
  121. panic(err)
  122. }
  123. return &RequestParam{
  124. Method: method,
  125. Path: path,
  126. Query: values,
  127. }
  128. }
  129. func MakeMultipartParam(method string, path string, info any, stream io.ReadCloser) *RequestParam {
  130. data, err := serder.ObjectToJSONEx(info)
  131. if err != nil {
  132. // 开发人员应该保证param是可序列化的
  133. panic(err)
  134. }
  135. pr, pw := io.Pipe()
  136. mw := multipart.NewWriter(pw)
  137. go func() {
  138. defer mw.Close()
  139. err := mw.WriteField("info", string(data))
  140. if err != nil {
  141. pw.CloseWithError(err)
  142. return
  143. }
  144. fw, err := mw.CreateFormFile("file", "file")
  145. if err != nil {
  146. pw.CloseWithError(err)
  147. return
  148. }
  149. _, err = io.Copy(fw, stream)
  150. if err != nil {
  151. pw.CloseWithError(err)
  152. return
  153. }
  154. }()
  155. headers := http.Header{}
  156. headers.Set("Content-Type", fmt.Sprintf("%s;boundary=%s", http2.ContentTypeMultiPart, mw.Boundary()))
  157. return &RequestParam{
  158. Method: method,
  159. Path: path,
  160. Header: headers,
  161. Body: &StreamBody{
  162. Stream: pr,
  163. LengthHint: -1,
  164. },
  165. }
  166. }
  167. type APIResponse interface {
  168. ParseResponse(resp *http.Response) error
  169. }
  170. type CodeDataResponse[T any] struct {
  171. Code string `json:"code"`
  172. Message string `json:"message"`
  173. Data T `json:"data"`
  174. }
  175. func (r *CodeDataResponse[T]) Unwarp() (T, error) {
  176. if r.Code == errorcode.OK {
  177. return r.Data, nil
  178. }
  179. var def T
  180. return def, &CodeMessageError{Code: r.Code, Message: r.Message}
  181. }
  182. func ParseCodeDataJSONResponse[T any](resp *http.Response, ret T) error {
  183. contType := resp.Header.Get("Content-Type")
  184. if strings.Contains(contType, http2.ContentTypeJSON) {
  185. var err error
  186. var r CodeDataResponse[T]
  187. r.Data = ret
  188. if err = serder.JSONToObjectStreamExRaw(resp.Body, &r); err != nil {
  189. return fmt.Errorf("parsing response: %w", err)
  190. }
  191. if r.Code != errorcode.OK {
  192. return &CodeMessageError{Code: r.Code, Message: r.Message}
  193. }
  194. return nil
  195. }
  196. cont, err := io2.ReadMost(resp.Body, 200)
  197. if err != nil {
  198. return fmt.Errorf("unknow response content type: %s, status: %d", contType, resp.StatusCode)
  199. }
  200. strCont := string(cont)
  201. return fmt.Errorf("unknow response content type: %s, status: %d, body[:200]: %s", contType, resp.StatusCode, strCont)
  202. }
  203. func ParseFileResponse(resp *http.Response) (string, io.ReadCloser, error) {
  204. contType := resp.Header.Get("Content-Type")
  205. if strings.Contains(contType, http2.ContentTypeJSON) {
  206. var err error
  207. var r CodeDataResponse[any]
  208. if err = serder.JSONToObjectStreamExRaw(resp.Body, &r); err != nil {
  209. return "", nil, fmt.Errorf("parsing response: %w", err)
  210. }
  211. return "", nil, &CodeMessageError{Code: r.Code, Message: r.Message}
  212. }
  213. _, params, err := mime.ParseMediaType(resp.Header.Get("Content-Disposition"))
  214. if err != nil {
  215. return "", nil, fmt.Errorf("parsing content disposition: %w", err)
  216. }
  217. return params["filename"], resp.Body, nil
  218. }
  219. func IsErrorCode(err error, code string) bool {
  220. if e, ok := err.(*CodeMessageError); ok {
  221. return e.Code == code
  222. }
  223. return false
  224. }