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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions