package rest_http import ( "bytes" "encoding/json" "io" "log" "mime/multipart" "net/http" "net/textproto" mjson "git.ali33.ru/fcg-xvii/go-tools/json" "git.ali33.ru/fcg-xvii/rest" ) func NewResponse() *Response { return &Response{ data: make(mjson.Map), files: make(map[string]io.ReadCloser), } } // Response реализует объект ответа type Response struct { data mjson.Map files map[string]io.ReadCloser err rest.IErrorArgs code int } func (s *Response) IsError() bool { return s.err != nil } // KeySet устанавливает значение в словаре ответа по ключу func (s *Response) KeySet(key string, val any) { s.data[key] = val } // FileSet устанавливает файл в словаре файлов по ключу func (s *Response) FileSet(name string, file io.ReadCloser) { s.files[name] = file } // Close закрывает ресурсы ответа после завершения отдачи серверу func (s *Response) Close() { for _, file := range s.files { file.Close() } } // Send отправляет запрос серверу func (s *Response) Send(writer any) rest.IErrorArgs { w := writer.(http.ResponseWriter) log.Println("SEND...") defer s.Close() if s.IsError() { w.Header().Set("Content-Type", "application/json") w.WriteHeader(s.code) w.Write([]byte(s.err.Error())) return nil } // Если есть файлы, то используем multipart if len(s.files) > 0 { var b bytes.Buffer writer := multipart.NewWriter(&b) w.Header().Set("Content-Type", writer.FormDataContentType()) // Добавляем JSON-данные как часть partHeader := make(textproto.MIMEHeader) partHeader.Set("Content-Type", "application/json") partHeader.Set("Content-Disposition", `form-data; name="data"`) dataPart, err := writer.CreatePart(partHeader) if err != nil { return rest.ErrorMessage("ErrResponsePartDataCreate", err.Error()) } if err := json.NewEncoder(dataPart).Encode(s.data); err != nil { return rest.ErrorMessage("ErrResponseDataJsonEncode", err.Error()) } // Добавляем файлы for filename, file := range s.files { part, err := writer.CreateFormFile("file", filename) if err != nil { return rest.ErrorMessage("ErrResponsePartFileCreate", err.Error()) } if _, err := io.Copy(part, file); err != nil { return rest.ErrorMessage("ErrResponsePartFileCopy", err.Error()) } } // Закрываем multipart writer if err := writer.Close(); err != nil { return rest.ErrorMessage("ErrResponsePartFileClose", err.Error()) } // Отправляем multipart response w.Header().Set("Content-Type", writer.FormDataContentType()) w.Write(b.Bytes()) } else { // Если нет файлов, просто отправляем JSON w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(s.data); err != nil { return rest.ErrorMessage("ErrResponseDataJsonEncode", err.Error()) } } return nil } // Успрешный ответ func ResponseSuccess(data mjson.Map, files map[string]io.ReadCloser) *Response { return &Response{ code: 200, data: data, files: files, } } func ResponseError(code int, err rest.IErrorArgs) *Response { return &Response{ code: code, err: err, } }