Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add Content-Type to multipart/form-data request #964

Closed
shpi0 opened this issue Dec 2, 2020 · 4 comments
Closed

Add Content-Type to multipart/form-data request #964

shpi0 opened this issue Dec 2, 2020 · 4 comments

Comments

@shpi0
Copy link

shpi0 commented Dec 2, 2020

Description
I'm making a controller that consumes multipart/form-data request with file and some extra information in json format.
I've made an project under spring-boot 2.3.3 and spriongdoc-openapi 1.5.0
Example of my project is here: https://github.com/shpi0/swagger-ui-test

Example of POST method with multipart data

@RouterOperation(
    path = PATH_MULTIPART,
    method = RequestMethod.POST,
    beanClass = TestController.class,
    beanMethod = "testMultipart",
    operation =
    @Operation(
            operationId = "testMultipart",
            parameters = {@Parameter(
                    name = "id",
                    required = true,
                    description = "ID of something",
                    in = ParameterIn.PATH)},
            requestBody = @RequestBody(
                    description = "Create file",
                    required = true,
                    content = @Content(
                            mediaType = "multipart/form-data",
                            encoding = {
                                    @Encoding(name = "document", contentType = "application/json")
                            },
                            schema = @Schema(type = "object", implementation = MultipartRequestDto.class))),
            security = {@SecurityRequirement(name = "bearer-key")},
            responses = {
                    @ApiResponse(
                            responseCode = "200",
                            description = "Successful Operation",
                            content = @Content(
                                    mediaType = "application/json",
                                    schema = @Schema(implementation = ResponseDto.class)))
            }))

Current behaviour
Now, when I'm trying to execute query from Swagger-ui, it produces the following query:

------WebKitFormBoundary29u9fdl6VqOiM8pP
Content-Disposition: form-data; name="document"

{
  "name": "Some name",
  "folder_id": "28fae5d4-f4db-4fc5-9dc6-8571ab30731a"
}
------WebKitFormBoundary29u9fdl6VqOiM8pP
Content-Disposition: form-data; name="file"; filename="q.txt"
Content-Type: text/plain


------WebKitFormBoundary29u9fdl6VqOiM8pP--

Problem
Is it possible to add Content-Type for document part in Swagger-ui, as in example below?

------WebKitFormBoundary29u9fdl6VqOiM8pP
Content-Disposition: form-data; name="document"
Content-Type: application/json
{
  "name": "Some name",
  "folder_id": "28fae5d4-f4db-4fc5-9dc6-8571ab30731a"
}
------WebKitFormBoundary29u9fdl6VqOiM8pP
Content-Disposition: form-data; name="file"; filename="q.txt"
Content-Type: text/plain


------WebKitFormBoundary29u9fdl6VqOiM8pP--

Screenshots
Here is an example how I make the request in Postman:
Screenshot_1

@bnasslahsen
Copy link
Collaborator

Instead of using encoding, use encodings.

Note that Since version v1.5.0, a functional DSL has been introduced.
This can make your code less verbose.
You have samples here:

@shpi0
Copy link
Author

shpi0 commented Dec 2, 2020

@bnasslahsen Thanks for your reply. Could you explain, what do you mean by telling "use encodings"?
There is no "encodings" parameter for any annotations in the io.swagger.v3.oas.annotations package. For example:

public @interface Content {
    String mediaType() default "";
    ExampleObject[] examples() default {};
    Schema schema() default @Schema;
    ArraySchema array() default @ArraySchema;
    Encoding[] encoding() default {};
    Extension[] extensions() default {};
}

BTW, I did the same documentation using functional DSL. Result is same - no Content-Type for json added to request when you execute it from swagger-ui.

    return SpringdocRouteBuilder.route()
        .POST(DOCS_PATH,
            accept(MediaType.MULTIPART_FORM_DATA),
            documentController::saveDocument,
            ops -> ops
                .operationId("saveDocument")
                .parameter(parameterBuilder().in(ParameterIn.PATH).name("mdmId").required(true).description("test"))
                .requestBody(requestBodyBuilder()
                    .content(contentBuilder()
                        .mediaType("multipart/form-data")
                        .encoding(encodingBuilder().name("document").contentType("application/json"))
                        .schema(schemaBuilder().type("object").implementation(MultipartRequest.class)))))
        .build();

Screenshot_2

@bnasslahsen
Copy link
Collaborator

@shpi0,

You can generate multiple encodings using the encodings attribute withing the @Content annotation.
You should get a correct OpenAPI spec independently from the swagger-ui, which is done by springdoc-openapi.

Testing the encoding from the swagger-ui is not yet supported in the swagger-ui and already answred here: #820

@jearton
Copy link

jearton commented Jun 14, 2024

I resolved it for any @RequestPart parameter.

    @Bean
    public OperationCustomizer operationCustomizer(ConversionService conversionService, ObjectProvider<GroupedOpenApi> groupedOpenApis) {
        OperationCustomizer customizer = (operation, handlerMethod) -> {
            Optional.ofNullable(operation.getRequestBody())
                    .map(RequestBody::getContent)
                    .filter(content -> content.containsKey(MediaType.MULTIPART_FORM_DATA_VALUE))
                    .map(content -> content.get(MediaType.MULTIPART_FORM_DATA_VALUE))
                    .ifPresent(multipartFormData -> {
                        for (MethodParameter methodParameter : handlerMethod.getMethodParameters()) {
                            if (MultipartResolutionDelegate.isMultipartArgument(methodParameter)) {
                                // ignore MultipartFile parameters
                                continue;
                            }
                            RequestPart requestPart = methodParameter.getParameterAnnotation(RequestPart.class);
                            if (requestPart == null) {
                                // ignore parameters without @RequestPart annotation
                                continue;
                            }
                            if (conversionService.canConvert(TypeDescriptor.valueOf(String.class), new TypeDescriptor(methodParameter))) {
                                // ignore parameters that can be converted from String to a basic type by ObjectToStringHttpMessageConverter
                                continue;
                            }
                            String parameterName = requestPart.name();
                            if (!StringUtils.hasText(parameterName)) {
                                parameterName = methodParameter.getParameterName();
                            }
                            if (!StringUtils.hasText(parameterName)) {
                                parameterName = methodParameter.getParameter().getName();
                            }
                            if (StringUtils.hasText(parameterName)) {
                                multipartFormData.addEncoding(parameterName, new Encoding().contentType(MediaType.APPLICATION_JSON_VALUE));
                            }
                        }
                    });
            return operation;
        };
        groupedOpenApis.forEach(groupedOpenApi -> groupedOpenApi.getOperationCustomizers().add(customizer));
        return customizer;
    }

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants