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:
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user