我使用 Map 来获取本地化值,其中 locale 作为键, String 作为值。对于必填字段,我需要检查是否至少设置了必需的区域设置 - 或者至少设置了某个值。我已经实现了用于此类映射字段和相应验证器的验证注释。问题是,如何报告缺失值? UI 中用于绑定字段错误/值的属性路径每次都会出错:
// Domain object:
@LocalizationRequired
private Map<Locale, String> field;
// LocalizationRequiredValidator:
public boolean isValid(Map<Locale, String> map, ConstraintValidatorContext context) {
if (requiredLocales.isEmpty()) {
// Check that there exists any not null value
} else {
context.disableDefaultConstraintViolation();
boolean valid = true;
for (Locale requiredLocale : requiredLocales) {
if (map.get(requiredLocale) == null) { // e.g. fi
valid = false;
context.buildConstraintViolationWithTemplate("LocalizationRequired")
// These end up in wrong property path:
// .addNode(requiredLocale)
// --> field.fi
// .addNode("[" + requiredLocale + "]")
// --> field.[fi]
// .addNode(null).addNode(requiredLocale).inIterable()
// --> field.fi
// .addNode(null).addNode(null).inIterable().atKey(requiredLocale)
// --> field
.addConstraintViolation();
}
}
return valid;
}
}
此错误的正确路径是“field[fi]”,但看来我只能访问索引子属性。在这种情况下,对象本身被索引。我正在使用 Hibernate Validator。
我无法找到一种方法来报告元素级别索引字段的错误。 - 规范中是否忽略了这一点?
这就是我所做的:
我没有使用 Map,而是使用了一个“可嵌入”bean,其中包含所有受支持语言环境的实际字段(例如 LocalizedString(String fi、String en 等)。然后报告了如下违规行为:
context.buildConstraintViolationWithTemplate("LocalizationRequired")
.addNode(requiredLocale)
.addConstraintViolation();
这在我们的情况下是可行的,因为我们有一组预定义的受支持语言,但它不能扩展到具有任意索引的索引字段。
此外,Spring 的
LocalValidatorFactoryBean
或 Hibernate Validator 都不能正确支持可嵌入的验证。由于同一组件在具有不同验证要求的不同地方使用,因此我无法将 @Valid
与组件本身内的实际验证注释一起使用 - 至少在不支持 @Valid
上的验证组的情况下无法使用。
Spring 的
LocalValidatorFactoryBean
或 Hibernate Validator 的问题在于 invalidValue
的 ConstraintViolation
是 LocalizedString(“field”),而不是报告的错误嵌套字段(“field.fi”)的值。幸运的是,这可以通过覆盖 LocalValidatorFactoryBean.processConstraintViolations
来解决,方法是删除“自定义 FieldError 注册,其值来自 ConstraintViolation”并简单地通过 报告错误
errors.rejectValue(field, errorCode, errorArgs, violation.getMessage());
这样 Spring 使用给定的
invalidValue
来解析 field
。
这是一个非常有趣的问题。现在我没有时间亲自测试:(,但是这里的这个人:
似乎能够验证元素集合。 因此,如果您切换到 Collection 而不是 Map(这应该相当容易),例如:
class LocaleToString {
private Locale locale;
private String language;
}
@LocalizationRequired
List<LocaleToString> locales;
我认为你应该能够实现你想要的。
旧帖子,但我没有看到有人为地图添加答案。 如果您执行以下操作:
if (map.get(requiredLocale) == null) { // e.g. fi
valid = false;
context.buildConstraintViolationWithTemplate("LocalizationRequired")
.addPropertyNode("<map value>")
.inIterable().atKey(requiredLocale)
.addConstraintViolation();
}
它将产生
field[fi].<map value>
,这与您在 中看到的属性格式相同
Map<Locale, @NotNull String>