使用Spring MVC构建RESTful Web服务时,我在尝试结合Jackson JSON反序列化和JSR-303 Bean验证时遇到了棘手的情况。
JSR-303验证的一个好处是,可以返回与目标对象相关的所有验证错误,而不仅仅是在第一个约束违规时失败(虽然如果您想使用它,也有快速失败模式)。
想象一种场景,您有一个JSON有效负载,例如:
在这种情况下,我想返回一个“错误”响应,例如:
这几乎是我想要的,但不幸的是,JSR-303 注释,如 @NotNull 和 @Min,将无法识别我的 BindableValue 实现。
那么,有人能解释一下我如何以更简洁的方式解决这个问题吗?
JSR-303验证的一个好处是,可以返回与目标对象相关的所有验证错误,而不仅仅是在第一个约束违规时失败(虽然如果您想使用它,也有快速失败模式)。
想象一种场景,您有一个JSON有效负载,例如:
{
"firstname": "Joe",
"surname": "Bloggs",
"age": 25
}
然后你需要映射的对象:
public class Person {
@NotNull
private String firstname;
@NotNull
private String surname;
@Min(value = 0)
private int age;
...
}
我对这种设置的问题在于,如果客户端将字符串作为“年龄”的值发送,则整个Jackson反序列化过程将失败,无法具体定位失败原因(即我们永远不会进行验证)。然后想象一下,这个有效负载:
{
"age": "invalid"
}
在这种情况下,我想返回一个“错误”响应,例如:
{
"errors" : [
"error": {
"field": "firstname",
"message": "Must not be null",
},
"error": {
"field": "surname",
"message": "Must not be null",
},
"error": {
"field": "age",
"message": "Must be numeric value greater than or equal 0",
}
]
}
解决这个问题的简单方法是将“age”字段指定为“String”类型,然后在传递给持久层时转换该值。但是,我不太喜欢使用字符串来处理数据传输对象的想法 - 从保持干净的设计角度来看,这种做法似乎不太合适。
我想到的另一个选择是创建一个类,例如:
public abstract class BindableValue<T> {
private String stringValue;
private T value;
public T getValue() {
if(value == null) {
value = fromString(stringValue);
}
return value;
}
protected abstract T fromString(String stringValue);
}
我可以创建一些实现,例如IntegerValue、LongValue、DateTimeValue等,当然还需要为Jackson编写自定义类型适配器。然后我的Person类可以是这样的:
public class Person {
@NotNull
private StringValue firstname;
@NotNull
private StringValue surname;
@Min(value = 0)
private IntegerValue age;
...
}
这几乎是我想要的,但不幸的是,JSR-303 注释,如 @NotNull 和 @Min,将无法识别我的 BindableValue 实现。
那么,有人能解释一下我如何以更简洁的方式解决这个问题吗?