Apply HttpMessageConverter auto-config to form part converters

Closes gh-3525
This commit is contained in:
Andy Wilkinson 2015-10-14 17:14:53 +01:00
parent 30f24eac0a
commit f770dbab52
2 changed files with 127 additions and 13 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +16,7 @@
package org.springframework.boot.autoconfigure.web;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -24,9 +25,11 @@ import java.util.Iterator;
import java.util.List;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.AbstractXmlHttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@ -45,6 +48,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupp
*
* @author Dave Syer
* @author Phillip Webb
* @author Andy Wilkinson
* @see #HttpMessageConverters(HttpMessageConverter...)
* @see #HttpMessageConverters(Collection)
* @see #getConverters()
@ -97,25 +101,37 @@ public class HttpMessageConverters implements Iterable<HttpMessageConverter<?>>
*/
public HttpMessageConverters(boolean addDefaultConverters,
Collection<HttpMessageConverter<?>> converters) {
List<HttpMessageConverter<?>> combined = getCombinedConverters(converters,
addDefaultConverters ? getDefaultConverters()
: Collections.<HttpMessageConverter<?>>emptyList());
combined = postProcessConverters(combined);
this.converters = Collections.unmodifiableList(combined);
}
private List<HttpMessageConverter<?>> getCombinedConverters(
Collection<HttpMessageConverter<?>> converters,
List<HttpMessageConverter<?>> defaultConverters) {
List<HttpMessageConverter<?>> combined = new ArrayList<HttpMessageConverter<?>>();
List<HttpMessageConverter<?>> processing = new ArrayList<HttpMessageConverter<?>>(
converters);
if (addDefaultConverters) {
for (HttpMessageConverter<?> defaultConverter : getDefaultConverters()) {
Iterator<HttpMessageConverter<?>> iterator = processing.iterator();
while (iterator.hasNext()) {
HttpMessageConverter<?> candidate = iterator.next();
if (isReplacement(defaultConverter, candidate)) {
combined.add(candidate);
iterator.remove();
}
for (HttpMessageConverter<?> defaultConverter : defaultConverters) {
Iterator<HttpMessageConverter<?>> iterator = processing.iterator();
while (iterator.hasNext()) {
HttpMessageConverter<?> candidate = iterator.next();
if (isReplacement(defaultConverter, candidate)) {
combined.add(candidate);
iterator.remove();
}
combined.add(defaultConverter);
}
combined.add(defaultConverter);
if (defaultConverter instanceof AllEncompassingFormHttpMessageConverter) {
configurePartConverters(
(AllEncompassingFormHttpMessageConverter) defaultConverter,
converters);
}
}
combined.addAll(0, processing);
combined = postProcessConverters(combined);
this.converters = Collections.unmodifiableList(combined);
return combined;
}
private boolean isReplacement(HttpMessageConverter<?> defaultConverter,
@ -128,6 +144,28 @@ public class HttpMessageConverters implements Iterable<HttpMessageConverter<?>>
return ClassUtils.isAssignableValue(defaultConverter.getClass(), candidate);
}
private void configurePartConverters(
AllEncompassingFormHttpMessageConverter formConverter,
Collection<HttpMessageConverter<?>> converters) {
List<HttpMessageConverter<?>> partConverters = extractPartConverters(
formConverter);
List<HttpMessageConverter<?>> combinedConverters = getCombinedConverters(
converters, partConverters);
combinedConverters = postProcessPartConverters(combinedConverters);
formConverter.setPartConverters(combinedConverters);
}
private List<HttpMessageConverter<?>> extractPartConverters(
AllEncompassingFormHttpMessageConverter formConverter) {
Field field = ReflectionUtils.findField(
AllEncompassingFormHttpMessageConverter.class, "partConverters");
ReflectionUtils.makeAccessible(field);
@SuppressWarnings("unchecked")
List<HttpMessageConverter<?>> partConverters = (List<HttpMessageConverter<?>>) ReflectionUtils
.getField(field, formConverter);
return partConverters;
}
/**
* Method that can be used to post-process the {@link HttpMessageConverter} list
* before it is used.
@ -139,6 +177,19 @@ public class HttpMessageConverters implements Iterable<HttpMessageConverter<?>>
return converters;
}
/**
* Method that can be used to post-process the {@link HttpMessageConverter} list
* before it is used to configure the part converters of
* {@link AllEncompassingFormHttpMessageConverter}.
* @param converters a mutable list of the converters that will be used.
* @return the final converts list to use
* @since 1.3.0
*/
protected List<HttpMessageConverter<?>> postProcessPartConverters(
List<HttpMessageConverter<?>> converters) {
return converters;
}
private List<HttpMessageConverter<?>> getDefaultConverters() {
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
if (ClassUtils.isPresent("org.springframework.web.servlet.config.annotation."

View File

@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.web;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
@ -32,6 +33,7 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConvert
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.test.util.ReflectionTestUtils;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
@ -100,6 +102,19 @@ public class HttpMessageConvertersTests {
assertEquals(converter2, converters.getConverters().get(1));
}
@Test
public void convertersAreAddedToFormPartConverter() {
HttpMessageConverter<?> converter1 = mock(HttpMessageConverter.class);
HttpMessageConverter<?> converter2 = mock(HttpMessageConverter.class);
List<HttpMessageConverter<?>> converters = new HttpMessageConverters(converter1,
converter2).getConverters();
List<HttpMessageConverter<?>> partConverters = extractFormPartConverters(
converters);
assertTrue(partConverters.contains(converter1));
assertEquals(converter1, partConverters.get(0));
assertEquals(converter2, partConverters.get(1));
}
@Test
public void postProcessConverters() throws Exception {
HttpMessageConverters converters = new HttpMessageConverters() {
@ -129,4 +144,52 @@ public class HttpMessageConvertersTests {
MappingJackson2HttpMessageConverter.class)));
}
@Test
public void postProcessPartConverters() throws Exception {
HttpMessageConverters converters = new HttpMessageConverters() {
@Override
protected List<HttpMessageConverter<?>> postProcessPartConverters(
List<HttpMessageConverter<?>> converters) {
for (Iterator<HttpMessageConverter<?>> iterator = converters
.iterator(); iterator.hasNext();) {
if (iterator
.next() instanceof MappingJackson2XmlHttpMessageConverter) {
iterator.remove();
}
}
return converters;
};
};
List<Class<?>> converterClasses = new ArrayList<Class<?>>();
for (HttpMessageConverter<?> converter : extractFormPartConverters(
converters.getConverters())) {
converterClasses.add(converter.getClass());
}
assertThat(converterClasses,
equalTo(Arrays.<Class<?>>asList(ByteArrayHttpMessageConverter.class,
StringHttpMessageConverter.class,
ResourceHttpMessageConverter.class,
SourceHttpMessageConverter.class,
MappingJackson2HttpMessageConverter.class)));
}
@SuppressWarnings("unchecked")
private List<HttpMessageConverter<?>> extractFormPartConverters(
List<HttpMessageConverter<?>> converters) {
AllEncompassingFormHttpMessageConverter formConverter = findFormConverter(
converters);
return (List<HttpMessageConverter<?>>) ReflectionTestUtils.getField(formConverter,
"partConverters");
}
private AllEncompassingFormHttpMessageConverter findFormConverter(
Collection<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof AllEncompassingFormHttpMessageConverter) {
return (AllEncompassingFormHttpMessageConverter) converter;
}
}
return null;
}
}