java - Spring无法从复杂对象构造查询参数以用于REST模板
问题描述
这个问题和Spring 的 RestTemplate有点不同:complex object to query params 假设有一个请求以对象的形式取一些参数:
@GetMapping("getRoute")
public ActionResult<Response> get(@Validated GetRequest req) {
//do some logic
}
请求本身如下所示:
public class GetRequest{
private String firstField;
private Long secondField;
private ComplexObject thirdField;
private static class ComplexObject{
private String subFirstField;
private Long subSecondField;
}
}
因此,当我从 RestTemplate 使用此对象执行查询时,我想获得这样的 URI:
/getRoute?firstField=val&secondField=val&thirdField.subFirstField=val&thirdField.subSecondField=val
我怎样才能做到这一点?对象绝对可以是任何东西。更大的问题是如何将这样的 this 对象转换为 UriComponentsBuilder 的 MultiValueMap。
如果它是一个 POST 请求,解决方案会很简单,但我需要它用于 GET。
我只知道Springfox库在生成Swagger API的时候就是用这种方式的,但是里面的逻辑太复杂了。我的场景:
@SuppressWarnings("unchecked")
public <T> ActionResult<T> getAction(String relativeUrl, Class<T> responseType, @Nullable Object paramsObject) {
UriComponentsBuilder builder = createUriComponentsBuilder(relativeUrl, paramsObject);
final ApiErrorCode errorCode;
try {
ResponseEntity<ActionResult> responseEntity = this.restTemplate.getForEntity(builder.build().toUri(), ActionResult.class);
ActionResult responseBody = responseEntity.getBody();
if (!Objects.requireNonNull(responseBody).isSuccess()) {
return responseBody;
}
return ActionResult.ok(this.mapper.convertValue(responseBody.getValue(), responseType));
} catch (RestClientException | HttpMessageNotReadableException e) {
errorCode = ApiErrorCode.API_CONNECTION_ERROR;
} catch (Exception e) {
errorCode = ApiErrorCode.INTERNAL_SERVER_ERROR;
}
return ActionResult.fail(errorCode);
}
private UriComponentsBuilder createUriComponentsBuilder(String relativeUrl, @Nullable Object object) {
String url = this.baseUrl;
if (StringUtils.hasText(relativeUrl)) {
url += relativeUrl;
}
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
if (object != null) {
builder.queryParams(this.convertToMultiValueMap(object));
}
return builder;
}
private MultiValueMap<String, String> convertToMultiValueMap(Object object) {
//todo object to params
}
解决方案
我不知道为什么我投了反对票。但我试图让我的实现,也许某处可能会出现无法预料的错误。
public static LinkedMultiValueMap<String, String> toUrlParams(Object value) {
final LinkedMultiValueMap<String, String> params = new LinkedMultiValueMap<>();
final List<Field> declaredFields = getAllFields(value);
for (Field field : declaredFields) {
ReflectionUtils.makeAccessible(field);
final String name = field.getName();
final Object fieldVal = ReflectionUtils.getField(field, value);
mapFields(params, name, fieldVal);
}
return params;
}
private static List<Field> getAllFields(Object t) {
final List<Field> fields = new ArrayList<>();
Class clazz = t.getClass();
while (clazz.getSuperclass() != null) {
final Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
final int modifiers = field.getModifiers();
if (!(Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers))) {
fields.add(field);
}
}
clazz = clazz.getSuperclass();
}
return fields;
}
private static void mapFields(LinkedMultiValueMap<String, String> params,
String fieldName,
@Nullable Object fieldVal) {
if (fieldVal != null) {
final Class<?> fieldClass = fieldVal.getClass();
if (BeanUtils.isSimpleValueType(fieldClass) || fieldVal instanceof Number || fieldVal instanceof UUID) {
if (fieldClass.isEnum()) {
params.add(fieldName, ((Enum) fieldVal).name());
} else {
params.add(fieldName, fieldVal.toString());
}
} else {
if (fieldVal instanceof Map) {
throw new IllegalArgumentException("Map is not allowed for url params");
}
if (fieldVal instanceof List) {
final Iterator iterator = ((Iterable) fieldVal).iterator();
int i = 0;
while (iterator.hasNext()) {
final Object iterElement = iterator.next();
mapFields(params, fieldName + "[" + i + "]", iterElement);
i++;
}
} else {
if (fieldVal instanceof Set) {
for (Object iterElement : ((Iterable) fieldVal)) {
mapFields(params, fieldName, iterElement);
}
} else {
if (fieldVal instanceof Collection) {
throw new IllegalArgumentException("Unknown collection, expected List or Set, but was " + fieldVal.getClass());
}
if (fieldVal.getClass().isArray()) {
final int length = Array.getLength(fieldVal);
for (int i = 0; i < length; i++) {
Object arrayElement = Array.get(fieldVal, i);
mapFields(params, fieldName + "[" + i + "]", arrayElement);
}
} else {
final List<Field> declaredFields = getAllFields(fieldVal);
for (Field field : declaredFields) {
ReflectionUtils.makeAccessible(field);
final String name = field.getName();
final Object nestedField = ReflectionUtils.getField(field, fieldVal);
mapFields(params, fieldName + "." + name, nestedField);
}
}
}
}
}
}
}
推荐阅读
- excel - Excel return INDEX results horizontally with multiple variables
- javascript - Parse SVG path from string in JavaScript
- informatica-powercenter - update/delete transformations on same target field
- r - How can I have a fast timer in R?
- import - Mule 4 Project Anypoint - import external Jar with Flows and Java
- xamarin.forms - Xamarin navigation bar has unwanted default icon, impossible to alter or remove
- sql-server - New user account login issues in SQL Server 2014
- mysql - Objection.js how to filter a model based on relationship
- python - QtQuick text search autocomplete using google autocomplete
- python - How to extract text between the matching pattern in python