swagger-ui icon indicating copy to clipboard operation
swagger-ui copied to clipboard

Default `Content-Type` not applied to complex object parts in `multipart/form-data`

Open heguro opened this issue 2 years ago • 1 comments

Q&A

  • OS: macOS
  • Browser: Firefox
  • Version: 122
  • Method of installation: swagger-ui-dist from unpkg
  • Swagger-UI version: 5.11.2
  • Swagger/OpenAPI version: OpenAPI 3.0.3

Content & configuration

Example Swagger/OpenAPI definition:

openapi: 3.0.3
servers:
- url: http://127.0.0.1:8080
paths:
  /profile:
    post:
      requestBody:
        content:
          multipart/form-data:
            schema:
              required:
              - address
              - id
              - profileImage
              type: object
              properties:
                id:
                  type: string
                address:
                  type: object
                  properties:
                    street:
                      type: string
                    city:
                      type: string
                profileImage:
                  type: string
                  format: binary
      responses:
        "200":
          description: OK

Swagger-UI configuration options:

SwaggerUIBundle({
  url: 'http://127.0.0.1:8080/api-docs.yaml',
  dom_id: '#swagger-ui',
  presets: [
    SwaggerUIBundle.presets.apis,
    SwaggerUIStandalonePreset
  ],
  layout: "StandaloneLayout",
})

Describe the bug you're encountering

According to the OpenAPI specification and the docs page, the default Content-Type for complex values (objects) in a multipart/form-data request should be application/json. However, in the provided example, the address property, which is an object, does not have application/json as its Content-Type in the generated request and curl command.

Currently, it's possible to manually set encoding.address.contentType in the definition as application/json to generate the expected curl command (thanks to issue #5356 was fixed). However, based on the OpenAPI specification, I believe type=application/json should automatically be applied even if encoding is not explicitly included in the definition.

To reproduce...

Steps to reproduce the behavior:

  1. Fill the blanks for the /profile request
  2. Click on 'Execute'
  3. Generated curl command and payload are:
curl -X 'POST' \
  'http://127.0.0.1:8080/profile' \
  -H 'accept: */*' \
  -H 'Content-Type: multipart/form-data' \
  -F 'id=string' \
  -F 'address={
  "street": "string",
  "city": "string"
}' \
  -F '[email protected];type=text/plain'
-----------------------------306494087715324971512255078872
Content-Disposition: form-data; name="id"

string
-----------------------------306494087715324971512255078872
Content-Disposition: form-data; name="address"

{
  "street": "string",
  "city": "string"
}
-----------------------------306494087715324971512255078872
Content-Disposition: form-data; name="profileImage"; filename="zerobyte.txt"
Content-Type: text/plain


-----------------------------306494087715324971512255078872--

Expected behavior

 curl -X 'POST' \
   'http://127.0.0.1:8080/profile' \
   -H 'accept: */*' \
   -H 'Content-Type: multipart/form-data' \
   -F 'id=string' \
   -F 'address={
   "street": "string",
   "city": "string"
-}' \
+};type=application/json' \
   -F '[email protected];type=text/plain'
 -----------------------------306494087715324971512255078872
 Content-Disposition: form-data; name="id"
 
 string
 -----------------------------306494087715324971512255078872
 Content-Disposition: form-data; name="address"
+Content-Type: application/json
 
 {
   "street": "string",
   "city": "string"
 }
 -----------------------------306494087715324971512255078872
 Content-Disposition: form-data; name="profileImage"; filename="zerobyte.txt"
 Content-Type: text/plain
 
 
 -----------------------------306494087715324971512255078872--

Additional context or thoughts

EDIT: this is part (maybe duplicate) of #6462

heguro avatar Feb 04 '24 09:02 heguro

Also bumped into this issue with a mixed multipart/form-data payload containing both files and a JSON options object. It seems that adding the encoding[fieldName].contentType fixes the CURL call, however it also causes Swagger to send a filename: "blob" in the Content-Disposition header.

OK CURL command:

curl -X 'POST' \
  'http://localhost:3000/pdf/generate/' \
  -H 'accept: */*' \
  -H 'Content-Type: multipart/form-data' \
  -F 'options={
  "world": "string"
};type=application/json'

SwaggerUI request payload

------WebKitFormBoundarynwJpnsbiqaJv4Ljy
Content-Disposition: form-data; name="options"; filename="blob"
Content-Type: application/json

{
  "world": "string"
}
------WebKitFormBoundarynwJpnsbiqaJv4Ljy--

Notice the filename="blob" appended to the Content-Disposition header.

Expected

------WebKitFormBoundarynwJpnsbiqaJv4Ljy
Content-Disposition: form-data; name="options";
Content-Type: application/json

{
  "world": "string"
}
------WebKitFormBoundarynwJpnsbiqaJv4Ljy--

This causes subsequent multipart parsing to think of options as a file rather than an object.

I think we should at least have a coherent Curl command and Execute action. There may be multiple issues at play, setting Content-Type should probably not trigger an automatic filename although it was referenced as mandatory, and we shouldn't need to set up Content-Type for complex objects in the first place.

I will try to narrow down what is happening with the fetch request :)

Titou325 avatar Mar 28 '24 19:03 Titou325