From 9e5610488f8b649856a72cd25695fc071b0ca1c4 Mon Sep 17 00:00:00 2001 From: Aliaksei Zhuk Date: Thu, 10 Dec 2020 13:48:45 +0300 Subject: [PATCH] =?UTF-8?q?[Go]=20Fix=20for=20'Invalid=20code=20for=20file?= =?UTF-8?q?s=20array=20in=20multipart/form-data=20request'=E2=80=A6=20(#81?= =?UTF-8?q?03)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix for 'Invalid code for files array in multipart/form-data request' (OpenAPITools#8093) * Executed ensure-up-to-date * Replaced spaces with tabs. --- .../codegen/languages/GoServerCodegen.java | 2 +- .../go-server/controller-api.mustache | 2 +- .../main/resources/go-server/routers.mustache | 57 +++++++++++++++---- .../petstore/go-api-server/go/routers.go | 49 +++++++++++++--- 4 files changed, 88 insertions(+), 22 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoServerCodegen.java index 2c216ab7827..95dc5e40826 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/GoServerCodegen.java @@ -228,7 +228,7 @@ public class GoServerCodegen extends AbstractGoCodegen { for (CodegenOperation operation : operations) { for (CodegenParameter param : operation.allParams) { // import "os" if the operation uses files - if (!addedOSImport && "*os.File".equals(param.dataType)) { + if (!addedOSImport && ("*os.File".equals(param.dataType) || ("[]*os.File".equals(param.dataType)))) { imports.add(createMapping("import", "os")); addedOSImport = true; } diff --git a/modules/openapi-generator/src/main/resources/go-server/controller-api.mustache b/modules/openapi-generator/src/main/resources/go-server/controller-api.mustache index 7b8a0913030..ecf77d9efee 100644 --- a/modules/openapi-generator/src/main/resources/go-server/controller-api.mustache +++ b/modules/openapi-generator/src/main/resources/go-server/controller-api.mustache @@ -66,7 +66,7 @@ func (c *{{classname}}Controller) {{nickname}}(w http.ResponseWriter, r *http.Re } {{/isInteger}}{{^isLong}}{{^isInteger}} {{paramName}} := {{#isArray}}strings.Split({{/isArray}}query.Get("{{paramName}}"){{#isArray}}, ","){{/isArray}}{{/isInteger}}{{/isLong}}{{/isQueryParam}}{{#isFormParam}}{{#isFile}} - {{paramName}}, err := ReadFormFileToTempFile(r, "{{paramName}}") + {{#isArray}}{{paramName}}, err := ReadFormFilesToTempFiles(r, "{{paramName}}"){{/isArray}}{{^isArray}}{{paramName}}, err := ReadFormFileToTempFile(r, "{{paramName}}"){{/isArray}} if err != nil { w.WriteHeader(500) return diff --git a/modules/openapi-generator/src/main/resources/go-server/routers.mustache b/modules/openapi-generator/src/main/resources/go-server/routers.mustache index f89bae068c8..aba82ab5bbb 100644 --- a/modules/openapi-generator/src/main/resources/go-server/routers.mustache +++ b/modules/openapi-generator/src/main/resources/go-server/routers.mustache @@ -7,9 +7,9 @@ import ( "net/http" "os" "strconv" - {{#featureCORS}} + {{#featureCORS}} "github.com/gorilla/handlers" - {{/featureCORS}} + {{/featureCORS}} "github.com/gorilla/mux" ) @@ -25,7 +25,7 @@ type Route struct { type Routes []Route // Router defines the required methods for retrieving api routes -type Router interface { +type Router interface { Routes() Routes } @@ -37,9 +37,9 @@ func NewRouter(routers ...Router) *mux.Router { var handler http.Handler handler = route.HandlerFunc handler = Logger(handler, route.Name) - {{#featureCORS}} + {{#featureCORS}} handler = handlers.CORS()(handler) - {{/featureCORS}} + {{/featureCORS}} router. Methods(route.Method). @@ -66,28 +66,61 @@ func EncodeJSONResponse(i interface{}, status *int, w http.ResponseWriter) error // ReadFormFileToTempFile reads file data from a request form and writes it to a temporary file func ReadFormFileToTempFile(r *http.Request, key string) (*os.File, error) { - r.ParseForm() - formFile, _, err := r.FormFile(key) + _, fileHeader, err := r.FormFile(key) + if err != nil { + return nil, err + } + + return readFileHeaderToTempFile(fileHeader) +} + +// ReadFormFilesToTempFiles reads files array data from a request form and writes it to a temporary files +func ReadFormFilesToTempFiles(r *http.Request, key string) ([]*os.File, error) { + if err := r.ParseMultipartForm(32 << 20); err != nil { + return nil, err + } + + files := make([]*os.File, 0, len(r.MultipartForm.File[key])) + + for _, fileHeader := range r.MultipartForm.File[key] { + file, err := readFileHeaderToTempFile(fileHeader) + if err != nil { + return nil, err + } + + files = append(files, file) + } + + return files, nil +} + +// readFileHeaderToTempFile reads multipart.FileHeader and writes it to a temporary file +func readFileHeaderToTempFile(fileHeader *multipart.FileHeader) (*os.File, error) { + formFile, err := fileHeader.Open() if err != nil { return nil, err } defer formFile.Close() - file, err := ioutil.TempFile("tmp", key) - if err != nil { - return nil, err - } - defer file.Close() fileBytes, err := ioutil.ReadAll(formFile) if err != nil { return nil, err } + file, err := ioutil.TempFile("", fileHeader.Filename) + if err != nil { + return nil, err + } + + defer file.Close() + file.Write(fileBytes) + return file, nil } + // parseInt64Parameter parses a sting parameter to an int64 func parseInt64Parameter(param string) (int64, error) { return strconv.ParseInt(param, 10, 64) diff --git a/samples/server/petstore/go-api-server/go/routers.go b/samples/server/petstore/go-api-server/go/routers.go index 37af1301ef9..9efdd18e3d8 100644 --- a/samples/server/petstore/go-api-server/go/routers.go +++ b/samples/server/petstore/go-api-server/go/routers.go @@ -30,7 +30,7 @@ type Route struct { type Routes []Route // Router defines the required methods for retrieving api routes -type Router interface { +type Router interface { Routes() Routes } @@ -68,28 +68,61 @@ func EncodeJSONResponse(i interface{}, status *int, w http.ResponseWriter) error // ReadFormFileToTempFile reads file data from a request form and writes it to a temporary file func ReadFormFileToTempFile(r *http.Request, key string) (*os.File, error) { - r.ParseForm() - formFile, _, err := r.FormFile(key) + _, fileHeader, err := r.FormFile(key) + if err != nil { + return nil, err + } + + return readFileHeaderToTempFile(fileHeader) +} + +// ReadFormFilesToTempFiles reads files array data from a request form and writes it to a temporary files +func ReadFormFilesToTempFiles(r *http.Request, key string) ([]*os.File, error) { + if err := r.ParseMultipartForm(32 << 20); err != nil { + return nil, err + } + + files := make([]*os.File, 0, len(r.MultipartForm.File[key])) + + for _, fileHeader := range r.MultipartForm.File[key] { + file, err := readFileHeaderToTempFile(fileHeader) + if err != nil { + return nil, err + } + + files = append(files, file) + } + + return files, nil +} + +// readFileHeaderToTempFile reads multipart.FileHeader and writes it to a temporary file +func readFileHeaderToTempFile(fileHeader *multipart.FileHeader) (*os.File, error) { + formFile, err := fileHeader.Open() if err != nil { return nil, err } defer formFile.Close() - file, err := ioutil.TempFile("tmp", key) - if err != nil { - return nil, err - } - defer file.Close() fileBytes, err := ioutil.ReadAll(formFile) if err != nil { return nil, err } + file, err := ioutil.TempFile("", fileHeader.Filename) + if err != nil { + return nil, err + } + + defer file.Close() + file.Write(fileBytes) + return file, nil } + // parseInt64Parameter parses a sting parameter to an int64 func parseInt64Parameter(param string) (int64, error) { return strconv.ParseInt(param, 10, 64)