Added an annotation for doing parameter conversion @ConvertWith. You can now specify a Spring Core Converter class to use to convert the query parameter String[] values coming in on the query string to the type needed in the query method parameter.

This commit is contained in:
Jon Brisbin
2012-08-07 17:05:43 -05:00
parent 42c86af9a2
commit 4abd842a86
5 changed files with 82 additions and 11 deletions

View File

@@ -0,0 +1,21 @@
package org.springframework.data.rest.repository.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.convert.converter.Converter;
/**
* @author Jon Brisbin
*/
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ConvertWith {
Class<? extends Converter<String[], ?>> value();
}

View File

@@ -4,6 +4,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
@@ -32,6 +33,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -57,6 +59,7 @@ import org.springframework.data.rest.repository.RepositoryExporter;
import org.springframework.data.rest.repository.RepositoryExporterSupport;
import org.springframework.data.rest.repository.RepositoryMetadata;
import org.springframework.data.rest.repository.RepositoryNotFoundException;
import org.springframework.data.rest.repository.annotation.ConvertWith;
import org.springframework.data.rest.repository.annotation.RestResource;
import org.springframework.data.rest.repository.context.AfterDeleteEvent;
import org.springframework.data.rest.repository.context.AfterLinkDeleteEvent;
@@ -538,6 +541,8 @@ public class RepositoryRestController
return notFoundResponse(request);
}
Annotation[][] annotations = queryMethod.method().getParameterAnnotations();
Class<?>[] paramTypes = queryMethod.paramTypes();
String[] paramNames = queryMethod.paramNames();
Object[] paramVals = new Object[paramTypes.length];
@@ -552,18 +557,24 @@ public class RepositoryRestController
continue;
}
String queryVal;
if(null == (queryVal = request.getServletRequest().getParameter(paramNames[i]))) {
String[] queryVals;
if(null == (queryVals = request.getServletRequest().getParameterValues(paramNames[i]))) {
continue;
}
if(String.class.isAssignableFrom(paramTypes[i])) {
// Param type is a String
paramVals[i] = queryVal;
} else if(hasRepositoryMetadataFor(paramTypes[i])) {
Class<? extends Converter<String[], ?>> converter = null;
for(Annotation anno : annotations[i]) {
if(ConvertWith.class.isAssignableFrom(anno.getClass())) {
converter = ((ConvertWith)anno).value();
break;
}
}
String firstVal = (queryVals.length > 0 ? queryVals[0] : null);
if(hasRepositoryMetadataFor(paramTypes[i])) {
RepositoryMetadata paramRepoMeta = repositoryMetadataFor(paramTypes[i]);
// Complex parameter is a managed type
Serializable id = stringToSerializable(queryVal,
Serializable id = stringToSerializable(firstVal,
(Class<Serializable>)paramRepoMeta.entityMetadata()
.idAttribute()
.type());
@@ -573,13 +584,22 @@ public class RepositoryRestController
}
paramVals[i] = o;
} else if(null != converter) {
try {
paramVals[i] = converter.newInstance().convert(queryVals);
} catch(InstantiationException e) {
throw new IllegalArgumentException(e);
}
} else if(String.class.isAssignableFrom(paramTypes[i])) {
// Param type is a String
paramVals[i] = firstVal;
} else if(conversionService.canConvert(String.class, paramTypes[i])) {
// There's a converter from String -> param type
paramVals[i] = conversionService.convert(queryVal, paramTypes[i]);
paramVals[i] = conversionService.convert(firstVal, paramTypes[i]);
} else {
// Param type isn't a "simple" type or no converter exists, try JSON
try {
paramVals[i] = objectMapper.readValue(queryVal, paramTypes[i]);
paramVals[i] = objectMapper.readValue(firstVal, paramTypes[i]);
} catch(IOException e) {
throw new IllegalArgumentException(e);
}

View File

@@ -29,7 +29,7 @@ class QueryMethodsSpec extends BaseSpec {
then:
response.statusCode == HttpStatus.OK
body.links.size() == 2
body.links.size() == 3
}

View File

@@ -4,12 +4,17 @@ import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.repository.annotation.ConvertWith;
import org.springframework.data.rest.repository.annotation.RestResource;
/**
* @author Jon Brisbin <jbrisbin@vmware.com>
* Example {@link org.springframework.data.repository.CrudRepository} for dealing with a {@link Person}. Also uses the
* {@link RestResource} annotation to turn off the delete methods.
*
* @author Jon Brisbin
*/
@RestResource(path = "people", rel = "peeps")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
@@ -25,4 +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)")
@RestResource(path = "id")
Page<Person> findById(@Param("id") @ConvertWith(StringToListOfLongsConverter.class) List<Long> ids, Pageable pageable);
}

View File

@@ -0,0 +1,21 @@
package org.springframework.data.rest.test.webmvc;
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.convert.converter.Converter;
/**
* @author Jon Brisbin
*/
public class StringToListOfLongsConverter implements Converter<String[], List<Long>> {
@Override public List<Long> convert(String[] source) {
List<Long> longs = new ArrayList<Long>();
for(String s : source) {
longs.add(Long.parseLong(s));
}
return longs;
}
}