不幸的是,没有简单的方法可以做到这一点。但这里有一些可以帮助您的见解:
解析约束违规
来自ConstraintViolationException,你可以得到一组ConstraintViolation,暴露约束违规上下文:
-
ConstraintViolation#getLeafBean():如果是 bean 约束,则此方法返回应用该约束的 bean 实例。
-
ConstraintViolation#getPropertyPath():返回无效属性的路径。
从属性路径中,可以得到叶子节点:
Path propertyPath = constraintViolation.getPropertyPath();
Optional<Path.Node> leafNodeOptional =
StreamSupport.stream(propertyPath.spliterator(), false).reduce((a, b) -> b);
然后检查节点的类型是否为PROPERTY并得到它的名字:
String nodeName = null;
if (leafNodeOptional.isPresent()) {
Path.Node leafNode = leafNodeOptional.get();
if (ElementKind.PROPERTY == leafNode.getKind()) {
nodeName = leafNode.getName();
}
}
与杰克逊一起反思课堂
要从叶 bean 类获取可用的 JSON 属性,您可以使用 Jackson 进行内省(请参阅此answer和这个answer欲了解更多详情):
Class<?> beanClass = constraintViolation.getLeafBean().getClass();
JavaType javaType = mapper.getTypeFactory().constructType(beanClass);
BeanDescription introspection = mapper.getSerializationConfig().introspect(javaType);
List<BeanPropertyDefinition> properties = introspection.findProperties();
然后通过将叶节点名称与Field名字来自于BeanPropertyDefinition:
Optional<String> jsonProperty = properties.stream()
.filter(property -> nodeName.equals(property.getField().getName()))
.map(BeanPropertyDefinition::getName)
.findFirst();
使用 JAX-RS?
使用 JAX-RS(如果您正在使用它),您可以定义一个ExceptionMapper处理ConstraintViolationExceptions:
@Provider
public class ConstraintViolationExceptionMapper
implements ExceptionMapper<ConstraintViolationException> {
@Override
public Response toResponse(ConstraintViolationException exception) {
...
}
}
要使用ObjectMapper在你的ExceptionMapper,你可以提供一个ContextResolver<T> for it:
@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public ObjectMapperContextResolver() {
mapper = createObjectMapper();
}
@Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
private ObjectMapper createObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return mapper;
}
}
注入Providers界面在你的ExceptionMapper:
@Context
private Providers providers;
查找您的ContextResolver<T>然后得到ObjectMapper实例:
ContextResolver<ObjectMapper> resolver =
providers.getContextResolver(ObjectMapper.class, MediaType.WILDCARD_TYPE);
ObjectMapper mapper = resolver.getContext(ObjectMapper.class);
如果您有兴趣获得@XxxParam
名字可以参考这个answer.