package rest_http import ( "bytes" "encoding/json" "log" "net/http" "strings" mjson "git.ali33.ru/fcg-xvii/go-tools/json" "git.ali33.ru/fcg-xvii/rest" ) func New(app rest.IApplication, core any, responseHeaders map[string]string) *Rest { return &Rest{ app: app, core: core, responseHeaders: responseHeaders, } } type Rest struct { server rest.IServer app rest.IApplication core any responseHeaders map[string]string Debug bool } func (s *Rest) App() rest.IApplication { return s.app } func (s *Rest) Prepare(server rest.IServer, httpPrefix string) { server.HandleFunc(httpPrefix, s.handle) s.server = server } func responseNotFound(w http.ResponseWriter) { w.WriteHeader(404) } func responseError(w http.ResponseWriter, err rest.IErrorArgs, code int) { w.WriteHeader(code) w.Write([]byte(err.Error())) } // handle func (s *Rest) handle(w http.ResponseWriter, r *http.Request) { log.Println("HANDLE START...") // установка заголовков ответа for k, v := range s.responseHeaders { w.Header().Set(k, v) } // запросы prelight if r.Method == "OPTIONS" { return } // Инициализация rest Request rr := &RequestIn{ RequestIn: &rest.RequestIn{ IRequest: &rest.Request{ Type: rest.RequestTypeIn, Command: r.URL.Path, Data: mjson.Map{}, Files: make(rest.RequestFiles), }, GeneratorToken: s.server.TokenGenerate, ParserToken: s.server.TokenParse, Core: s.core, }, } // Парсим Bearer токен и извлекаем claims authHeader := r.Header.Get("Authorization") if authHeader != "" { if parts := strings.Split(authHeader, " "); len(parts) == 2 && parts[0] == "Bearer" { tokenString := parts[1] auth, err := s.server.TokenParse(tokenString) if err == nil { rr.auth = auth } } } // Если это многокомпонентный запрос, обрабатываем файлы if strings.Index(r.Header.Get("Content-Type"), "multipart/form-data") == 0 { if s.Debug { log.Println("request multipart") } err := r.ParseMultipartForm(32 << 20) // max memory 32MB, после этого файлы будут сохранены во временных файлах log.Println(err) if err != nil { responseError(w, rest.ErrorMessage("ErrMultipartParse", err.Error()), 500) return } multiPartForm := r.MultipartForm data, check := multiPartForm.Value["data"] if check { if s.Debug { log.Println("data accepted") } rData := rr.RData() err := json.NewDecoder(bytes.NewBuffer([]byte(data[0]))).Decode(&rData) if err != nil { responseError(w, rest.ErrorMessage("ErrMultipartDataParse", err.Error()), 500) return } if s.Debug { rData.LogPretty() } } files := rr.RFiles() for filename, headers := range multiPartForm.File { for _, header := range headers { file, err := header.Open() if err != nil { err := rest.NewError( "ErrMultipartFileParse", mjson.Map{ "filename": filename, "message": err.Error(), }, ) responseError(w, err, 500) } upFile := rest.NewReadCloserLen( file, header.Size, ) rFile := &rest.MimeFile{ Name: filename, MimeType: header.Header.Get("Content-Type"), File: upFile, } files[filename] = rFile } } defer rr.RClose() } else { if s.Debug { log.Println("request json") } data := rr.RData() err := json.NewDecoder(r.Body).Decode(&data) if err != nil { responseError(w, rest.ErrorMessage("ErrDataParse", err.Error()), 500) return } if s.Debug { data.LogPretty() } } log.Println("HANDLE MIDDLE...") defer log.Println("HANDLE FINISH...") // get command //command, check := s.commands.GetCommand(r.URL.Path) command, check := s.app.Executer(rr) if !check { responseNotFound(w) return } //log.Println(rr.RData) // serialize if err := rest.Serialize(rr.RData(), command); err != nil { responseError(w, err, 500) return } // validate if validator, check := command.(rest.IValidator); check { resp := validator.Validate(rr) if resp != nil { if err := resp.Write(w); err != nil { responseError(w, err, 500) } return } } // execute resp := command.Execute(rr) if err := resp.Write(w); err != nil { responseError(w, err, 500) } resp.RClose() }