Fix problem with the ConversionService not being sufficiently checked for converters when reading query parameters, requiring an alternative mechanism for doing parameter conversion.

This commit is contained in:
Jon Brisbin
2012-08-17 14:39:34 -05:00
committed by Jon Brisbin
parent b9b6a8fe92
commit 023ab6b885
5 changed files with 77 additions and 27 deletions

View File

@@ -2,6 +2,8 @@ package org.springframework.data.rest.repository;
import java.io.Serializable;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
import org.springframework.beans.factory.annotation.Autowired;
@@ -20,14 +22,14 @@ public class UriToDomainObjectResolver
implements Resolver<Object> {
@Autowired(required = false)
private ConversionService conversionService = new DefaultFormattingConversionService();
private List<ConversionService> conversionServices = Arrays.<ConversionService>asList(new DefaultFormattingConversionService());
public ConversionService getConversionService() {
return conversionService;
public List<ConversionService> getConversionServices() {
return conversionServices;
}
public UriToDomainObjectResolver setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
public UriToDomainObjectResolver setConversionServices(List<ConversionService> conversionServices) {
this.conversionServices = conversionServices;
return this;
}
@@ -56,11 +58,16 @@ public class UriToDomainObjectResolver
}
Class<? extends Serializable> idType = (Class<? extends Serializable>)entityMeta.idAttribute().type();
Serializable serId;
Serializable serId = null;
if(ClassUtils.isAssignable(idType, String.class)) {
serId = sId;
} else {
serId = conversionService.convert(sId, idType);
for(ConversionService cs : conversionServices) {
if(cs.canConvert(String.class, idType)) {
serId = cs.convert(sId, idType);
break;
}
}
}
return repo.findOne(serId);

View File

@@ -60,7 +60,7 @@ public class RepositoryAwareMappingHttpMessageConverter
private final ObjectMapper mapper = new ObjectMapper();
@Autowired(required = false)
protected ConversionService conversionService = new DefaultFormattingConversionService();
protected List<ConversionService> conversionServices = Arrays.<ConversionService>asList(new DefaultFormattingConversionService());
@Autowired(required = false)
protected List<RepositoryExporter> repositoryExporters = Collections.emptyList();
@Autowired(required = false)
@@ -84,12 +84,12 @@ public class RepositoryAwareMappingHttpMessageConverter
}
}
public ConversionService getConversionService() {
return conversionService;
public List<ConversionService> getConversionServices() {
return conversionServices;
}
public RepositoryAwareMappingHttpMessageConverter setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
public RepositoryAwareMappingHttpMessageConverter setConversionServices(List<ConversionService> conversionServices) {
this.conversionServices = conversionServices;
return this;
}
@@ -258,7 +258,13 @@ public class RepositoryAwareMappingHttpMessageConverter
}
Serializable serId = (Serializable)idAttr.get(value);
String sId = conversionService.convert(serId, String.class);
String sId = null;
for(ConversionService cs : conversionServices) {
if(cs.canConvert(idAttr.type(), String.class)) {
sId = cs.convert(serId, String.class);
break;
}
}
if(null == sId) {
sId = serId.toString();
}
@@ -291,7 +297,13 @@ public class RepositoryAwareMappingHttpMessageConverter
}
Serializable serId = (Serializable)idAttr.get(value);
String sId = conversionService.convert(serId, String.class);
String sId = null;
for(ConversionService cs : conversionServices) {
if(cs.canConvert(idAttr.type(), String.class)) {
sId = cs.convert(serId, String.class);
break;
}
}
if(null == sId) {
sId = serId.toString();
}

View File

@@ -33,8 +33,10 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.OptimisticLockingFailureException;
@@ -176,22 +178,28 @@ public class RepositoryRestController
/**
* Get the {@link ConversionService} in use by the controller.
*
* @return The internal {@link ConversionService}.
* @return The internal {@link ConversionService}s.
*/
public ConversionService getConversionService() {
return conversionService;
}
/**
* Add this {@link ConversionService} to the list of those being delegated to by the internal {@link
* Add these {@link ConversionService}s to the list of those being delegated to by the internal {@link
* DelegatingConversionService}. Although this method does an 'add', it is called 'set' to make it JavaBean-friendly.
*
* @param conversionService
* @param conversionServices
*/
@Autowired(required = false)
public void setConversionService(ConversionService conversionService) {
public void setConversionServices(List<ConversionService> conversionServices) {
if(null == conversionServices) {
return;
}
Collections.reverse(conversionServices);
if(null != conversionService) {
this.conversionService.addConversionServices(conversionService);
this.conversionService.addConversionServices(
conversionServices.toArray(new ConversionService[conversionServices.size()])
);
}
}
@@ -205,14 +213,14 @@ public class RepositoryRestController
}
/**
* @param conversionService
* @param conversionServices
*
* @return @this
*
* @see RepositoryRestController#setConversionService(org.springframework.core.convert.ConversionService)
* @see RepositoryRestController#setConversionServices(java.util.List)
*/
public RepositoryRestController conversionService(ConversionService conversionService) {
setConversionService(conversionService);
public RepositoryRestController conversionServices(List<ConversionService> conversionServices) {
setConversionServices(conversionServices);
return this;
}
@@ -589,6 +597,10 @@ public class RepositoryRestController
continue;
}
TypeDescriptor stringTypeDesc = TypeDescriptor.valueOf(String[].class);
MethodParameter methodParam = new MethodParameter(queryMethod.method(), i);
TypeDescriptor targetTypeDesc = new TypeDescriptor(methodParam);
Class<? extends Converter<String[], ?>> converter = null;
for(Annotation anno : annotations[i]) {
if(ConvertWith.class.isAssignableFrom(anno.getClass())) {
@@ -620,9 +632,9 @@ public class RepositoryRestController
} else if(String.class.isAssignableFrom(paramTypes[i])) {
// Param type is a String
paramVals[i] = firstVal;
} else if(conversionService.canConvert(String.class, paramTypes[i])) {
} else if(conversionService.canConvert(stringTypeDesc, targetTypeDesc)) {
// There's a converter from String -> param type
paramVals[i] = conversionService.convert(firstVal, paramTypes[i]);
paramVals[i] = conversionService.convert(queryVals, stringTypeDesc, targetTypeDesc);
} else {
// Param type isn't a "simple" type or no converter exists, try JSON
try {

View File

@@ -1,5 +1,7 @@
package org.springframework.data.rest.test.webmvc;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
@@ -7,9 +9,12 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.rest.webmvc.RepositoryRestConfiguration;
import org.springframework.data.rest.webmvc.RepositoryRestMvcConfiguration;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.orm.jpa.JpaDialect;
@@ -70,4 +75,18 @@ public class ApplicationConfig {
return new TestRepositoryEventListener();
}
@Bean public ConversionService customConversionService() {
DefaultFormattingConversionService cs = new DefaultFormattingConversionService();
cs.addConverter(new Converter<String[], List<Long>>() {
@Override public List<Long> convert(String[] source) {
List<Long> longs = new ArrayList<Long>(source.length);
for(String s : source) {
longs.add(Long.parseLong(s));
}
return longs;
}
});
return cs;
}
}

View File

@@ -30,8 +30,8 @@ public interface PersonRepository extends PagingAndSortingRepository<Person, Lon
@RestResource(path = "nameStartsWith", rel = "nameStartsWith")
Page findByNameStartsWith(@Param("name") String name, Pageable p);
@Query("select p from Person p where p.id in(:id)")
@Query("select p from Person p where p.id in(:ids)")
@RestResource(path = "id")
Page<Person> findById(@Param("id") @ConvertWith(StringToListOfLongsConverter.class) List<Long> ids, Pageable pageable);
Page<Person> findById(@Param("ids") List<Long> ids, Pageable pageable);
}