response.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package rest_http
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "io"
  6. "log"
  7. "mime/multipart"
  8. "net/http"
  9. "net/textproto"
  10. mjson "git.ali33.ru/fcg-xvii/go-tools/json"
  11. "git.ali33.ru/fcg-xvii/rest"
  12. )
  13. func NewResponse() *Response {
  14. return &Response{
  15. data: make(mjson.Map),
  16. files: make(map[string]io.ReadCloser),
  17. }
  18. }
  19. // Response реализует объект ответа
  20. type Response struct {
  21. data mjson.Map
  22. files map[string]io.ReadCloser
  23. err rest.IErrorArgs
  24. code int
  25. }
  26. func (s *Response) IsError() bool {
  27. return s.err != nil
  28. }
  29. // KeySet устанавливает значение в словаре ответа по ключу
  30. func (s *Response) KeySet(key string, val any) {
  31. s.data[key] = val
  32. }
  33. // FileSet устанавливает файл в словаре файлов по ключу
  34. func (s *Response) FileSet(name string, file io.ReadCloser) {
  35. s.files[name] = file
  36. }
  37. // Close закрывает ресурсы ответа после завершения отдачи серверу
  38. func (s *Response) Close() {
  39. for _, file := range s.files {
  40. file.Close()
  41. }
  42. }
  43. // Send отправляет запрос серверу
  44. func (s *Response) Send(writer any) rest.IErrorArgs {
  45. w := writer.(http.ResponseWriter)
  46. log.Println("SEND...")
  47. defer s.Close()
  48. if s.IsError() {
  49. w.Header().Set("Content-Type", "application/json")
  50. w.WriteHeader(s.code)
  51. w.Write([]byte(s.err.Error()))
  52. return nil
  53. }
  54. // Если есть файлы, то используем multipart
  55. if len(s.files) > 0 {
  56. var b bytes.Buffer
  57. writer := multipart.NewWriter(&b)
  58. w.Header().Set("Content-Type", writer.FormDataContentType())
  59. // Добавляем JSON-данные как часть
  60. partHeader := make(textproto.MIMEHeader)
  61. partHeader.Set("Content-Type", "application/json")
  62. partHeader.Set("Content-Disposition", `form-data; name="data"`)
  63. dataPart, err := writer.CreatePart(partHeader)
  64. if err != nil {
  65. return rest.ErrorMessage("ErrResponsePartDataCreate", err.Error())
  66. }
  67. if err := json.NewEncoder(dataPart).Encode(s.data); err != nil {
  68. return rest.ErrorMessage("ErrResponseDataJsonEncode", err.Error())
  69. }
  70. // Добавляем файлы
  71. for filename, file := range s.files {
  72. part, err := writer.CreateFormFile("file", filename)
  73. if err != nil {
  74. return rest.ErrorMessage("ErrResponsePartFileCreate", err.Error())
  75. }
  76. if _, err := io.Copy(part, file); err != nil {
  77. return rest.ErrorMessage("ErrResponsePartFileCopy", err.Error())
  78. }
  79. }
  80. // Закрываем multipart writer
  81. if err := writer.Close(); err != nil {
  82. return rest.ErrorMessage("ErrResponsePartFileClose", err.Error())
  83. }
  84. // Отправляем multipart response
  85. w.Header().Set("Content-Type", writer.FormDataContentType())
  86. w.Write(b.Bytes())
  87. } else {
  88. // Если нет файлов, просто отправляем JSON
  89. w.Header().Set("Content-Type", "application/json")
  90. if err := json.NewEncoder(w).Encode(s.data); err != nil {
  91. return rest.ErrorMessage("ErrResponseDataJsonEncode", err.Error())
  92. }
  93. }
  94. return nil
  95. }
  96. // Успрешный ответ
  97. func ResponseSuccess(data mjson.Map, files map[string]io.ReadCloser) *Response {
  98. return &Response{
  99. code: 200,
  100. data: data,
  101. files: files,
  102. }
  103. }
  104. func ResponseError(code int, err rest.IErrorArgs) *Response {
  105. return &Response{
  106. code: code,
  107. err: err,
  108. }
  109. }