Home > Blockchain >  Spring InvalidMediaTypeException: Invalid mime type "multipart/mixed; boundary==_123456789"
Spring InvalidMediaTypeException: Invalid mime type "multipart/mixed; boundary==_123456789"

Time:06-26

I use Spring to call a REST API which reply with header that contains "Content-Type: multipart/mixed; boundary==_123456789".

My problem is that Spring try to parse the Content-Type and throw InvalidMediaTypeException due to '=' in boundary token.

Is there a way to disable this checking (or any workaround) ?

These are my logs :

org.springframework.http.InvalidMediaTypeException: Invalid mime type "multipart/mixed; boundary==_11204303fda4403b6a72d61500081354": Invalid token character '=' in token "=_11204303fda4403b6a72d61500081354"
    at org.springframework.http.MediaType.parseMediaType(MediaType.java:620)
    at org.springframework.http.HttpHeaders.getContentType(HttpHeaders.java:992)
    at org.springframework.web.client.HttpMessageConverterExtractor.getContentType(HttpMessageConverterExtractor.java:136)
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:93)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1037)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1020)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:778)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:711)
    at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:468)

CodePudding user response:

RFC1341 applies to MIME types in email.

For HTTP the specification is in RFC2616 § 2.2, and that document specifies that "token separators" can be included as values only if the value is enclosed in quotes. It is possible that whatever is creating that boundary string is using the RFC1341 definition instead of the (correct) RFC2616 spec.

The header should be

multipart/mixed; boundary="=_11204303fda4403b6a72d61500081354"

BTW, the code that throws the exception is here in method checkToken(). That's in the latest version, looks like you have an earlier version of Spring.

CodePudding user response:

I found a way by using ClientHttpRequestInterceptor to modify the boundary parameter and add double quote.

public class MultipartMixedBoundaryRewritingInterceptor implements ClientHttpRequestInterceptor {

    private Pattern multipartMixedHeaderPattern = Pattern.compile("^multipart/mixed;\\s?boundary=(.*?)$");

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        ClientHttpResponse response = execution.execute(request, body);
        
        HttpHeaders responseHeaders = response.getHeaders();
        // Do not use .getHeaders().getContentType() since it checks everything and raises an exception on the invalid boundary
        String contentType = response.getHeaders().getFirst("Content-Type");
        Matcher matcher = multipartMixedHeaderPattern.matcher(contentType);
        if (contentType != null && matcher.matches()) {
            String boundaryParameter = matcher.group(1);
            try {
                responseHeaders.getContentType();
            } catch (IllegalArgumentException e) {
                if (boundaryParameter != null && !isQuotedString(boundaryParameter)) {
                    Map<String, String> parameters = new HashMap<>();
                    parameters.put("boundary", String.format("\"%s\"", boundaryParameter));
                    MediaType fixedContentType = new MediaType(MediaType.MULTIPART_MIXED, parameters);
                    response.getHeaders().setContentType(fixedContentType);
                }
            }
        }
        return response;
    }

    private boolean isQuotedString(String s) {
        return s.length() > 2 && ((s.startsWith("\"") && s.endsWith("\"")) || (s.startsWith("'") && s.endsWith("'")));
    }
}

And configuring the restTemplate

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        configureRestTemplateInterceptors(restTemplate);
        return restTemplate;
    }

    private void configureRestTemplateInterceptors(RestTemplate restTemplate) {
        List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
        if (CollectionUtils.isEmpty(interceptors)) {
                interceptors = new ArrayList<>();
        }
        interceptors.add(new MultipartMixedBoundaryRewritingInterceptor());
        restTemplate.setInterceptors(interceptors);
    }
}
  • Related