Search code examples
javaspringrestmultipart

Spring InvalidMediaTypeException: Invalid mime type "multipart/mixed; boundary==_123456789": Invalid token character '=' in token "=_123456789"


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)

Solution

  • 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);
        }
    }