package sdks import ( "bytes" "fmt" "io" "net/http" "net/url" "strings" "github.com/google/go-querystring/query" "gitlink.org.cn/cloudream/common/consts/errorcode" "gitlink.org.cn/cloudream/common/utils/http2" "gitlink.org.cn/cloudream/common/utils/io2" "gitlink.org.cn/cloudream/common/utils/serder" ) type CodeMessageError struct { Code string Message string } func (e *CodeMessageError) Error() string { return fmt.Sprintf("code: %s, message: %s", e.Code, e.Message) } type RequestParam struct { Method string Path string Query url.Values Header http.Header Body RequestBody } func (p *RequestParam) MakeRequest(baseURL string) (*http.Request, error) { var body io.ReadCloser bodyLen := int64(0) if p.Body != nil { body = p.Body.IntoStream() bodyLen = p.Body.Length() } req, err := http.NewRequest(p.Method, joinUrlUnsafe(baseURL, p.Path), body) if err != nil { return nil, err } req.ContentLength = bodyLen req.URL.RawQuery = p.Query.Encode() if p.Header != nil { req.Header = p.Header } return req, nil } func (p *RequestParam) Do(baseURL string, cli *http.Client) (*http.Response, error) { req, err := p.MakeRequest(baseURL) if err != nil { return nil, err } return cli.Do(req) } func joinUrlUnsafe(base string, path string) string { if strings.HasSuffix(base, "/") { if strings.HasPrefix(path, "/") { return base + path[1:] } return base + path } if strings.HasPrefix(path, "/") { return base + path } return base + "/" + path } type RequestBody interface { // 请求体长度,如果长度未知,返回-1 Length() int64 // 将内部值变成一个流,用于发送请求 IntoStream() io.ReadCloser } type StringBody struct { Value string } func (s *StringBody) Length() int64 { return int64(len(s.Value)) } func (s *StringBody) IntoStream() io.ReadCloser { return io.NopCloser(bytes.NewReader([]byte(s.Value))) } type BytesBody struct { Value []byte } func (b *BytesBody) Length() int64 { return int64(len(b.Value)) } func (b *BytesBody) IntoStream() io.ReadCloser { return io.NopCloser(bytes.NewReader(b.Value)) } type StreamBody struct { Stream io.ReadCloser LengthHint int64 // 长度提示,如果长度未知,可以设置为-1 } func (s *StreamBody) Length() int64 { return s.LengthHint } func (s *StreamBody) IntoStream() io.ReadCloser { return s.Stream } type APIRequest interface { MakeParam() *RequestParam } func MakeJSONParam(method string, path string, body any) *RequestParam { data, err := serder.ObjectToJSONEx(body) if err != nil { // 开发人员应该保证param是可序列化的 panic(err) } return &RequestParam{ Method: method, Path: path, Body: &BytesBody{Value: data}, } } func MakeQueryParam(method string, path string, q any) *RequestParam { values, err := query.Values(q) if err != nil { // 开发人员应该保证param是可序列化的 panic(err) } return &RequestParam{ Method: method, Path: path, Query: values, } } type APIResponse interface { ParseResponse(resp *http.Response) error } type CodeDataResponse[T any] struct { Code string `json:"code"` Message string `json:"message"` Data T `json:"data"` } func (r *CodeDataResponse[T]) Unwarp() (T, error) { if r.Code == errorcode.OK { return r.Data, nil } var def T return def, &CodeMessageError{Code: r.Code, Message: r.Message} } func ParseCodeDataJSONResponse[T any](resp *http.Response, ret T) error { contType := resp.Header.Get("Content-Type") if strings.Contains(contType, http2.ContentTypeJSON) { var err error var r CodeDataResponse[T] r.Data = ret if err = serder.JSONToObjectStreamExRaw(resp.Body, &r); err != nil { return fmt.Errorf("parsing response: %w", err) } if r.Code != errorcode.OK { return &CodeMessageError{Code: r.Code, Message: r.Message} } return nil } cont, err := io2.ReadMost(resp.Body, 200) if err != nil { return fmt.Errorf("unknow response content type: %s, status: %d", contType, resp.StatusCode) } strCont := string(cont) return fmt.Errorf("unknow response content type: %s, status: %d, body[:200]: %s", contType, resp.StatusCode, strCont) }