Skip to content

Wrong content-type for multipart requests on JSON parts (text/plain instead of application/json) #2813

@anessi

Description

@anessi

I have the following request mapping defined which includes 2 parts:

  • metadata: POJO (application/json)
  • files: multipart
    @RequestMapping(
        method = RequestMethod.POST,
        value = "/v1/files/bulk",
        produces = { "application/json" },
        consumes = "multipart/form-data"
    )
    ResponseEntity<Files> saveFiles(
        @Parameter(name = "metadata", description = "Array of file attributes", required = true) @Valid @RequestPart(value = "metadata", required = true) List<@Valid Metadata> metadata,
        @Parameter(name = "files", description = "Array of Files") @RequestPart(value = "files", required = false) List<MultipartFile> files
    );

which produces the following body:

--195903cbf58
Content-Disposition: form-data; name="metadata"
Content-Type: text/plain; charset=UTF-8

[{"fileName":"somefileName","hash":"somehash"}]
--195903cbf58
Content-Disposition: form-data; name="files"; filename=""
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary

--195903cbf58--

It shows that the content type of the JSON part is set to text/plain instead of application/json.
This is due to the fact that feign.form.multipart.DelegateWriter#parameterWriter created in feign.form.MultipartFormContentProcessor#MultipartFormContentProcessor is set to feign.form.multipart.SingleParameterWriter which is using a hard-coded content-type of text/plain.

I have noticed that the RequestTemplate that is used to write the JSON data actually has the correct headers set ("Content-Type: application/json"). However, this is ignored as the hard-coded value is used.

I would expect that the content type is taken from the RequestTemplate.

The only workaround that I found is to do a String replace on the body like this:

public class SpringFormEncoderWithContentTypeCorrection extends SpringFormEncoder {

    private static final String CONTENT_TYPE_STRING_TO_REPLACE = "Content-Type: text/plain; charset=";
    private static final String NEW_CONTENT_TYPE_STRING = "Content-Type: application/json; charset=";

    public SpringFormEncoderWithContentTypeCorrection(Encoder delegate) {
        super(delegate);
    }

    @Override
    public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
        super.encode(object, bodyType, template);
        if (template.body() != null) {
            // modify content type from 'text/plain' to 'application/json' for json parts
            String modifiedContent = (new String(template.body())).replace(CONTENT_TYPE_STRING_TO_REPLACE, NEW_CONTENT_TYPE_STRING);
            template.body(modifiedContent);
        }
    }
}

Configuration:

    @Bean
    @Primary
    @Scope("prototype")
    public Encoder feignFormEncoder(final ObjectFactory<HttpMessageConverters> messageConverters) {
        return new SpringFormEncoderWithContentTypeCorrection(new SpringEncoder(messageConverters));
    }

Versions used:

  • openfeign: 13.5
  • spring-cloud-openfeign-core: 4.2.0
  • Spring Boot: 3.4.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugUnexpected or incorrect behavior

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions