我有一个模型,当从前端发送请求时,我可以在控制器中使用@Valid进行验证:
@NotNull @Size(min=1, message="Name should be at least 1 character.") private String name; @NotNull @Pattern(regexp = "^https://github.com/.+/.+$", message = "Link to github should match https://github.com/USER/REPOSITORY") private String github;
但是现在,我还使用不含控制器的Jackson的ObjectMapper创建了一个对象。有没有一种方法可以在ObjectMapper中注册此验证,还是应该只检查setter中的变量?
BeanDeserializer反序列化后,您可以扩展和验证对象。要注册此bean使用SimpleModule。
BeanDeserializer
SimpleModule
具有验证的简单bean反序列化器:
class BeanValidationDeserializer extends BeanDeserializer { private final static ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); private final Validator validator = factory.getValidator(); public BeanValidationDeserializer(BeanDeserializerBase src) { super(src); } @Override public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { Object instance = super.deserialize(p, ctxt); validate(instance); return instance; } private void validate(Object instance) { Set<ConstraintViolation<Object>> violations = validator.validate(instance); if (violations.size() > 0) { StringBuilder msg = new StringBuilder(); msg.append("JSON object is not valid. Reasons (").append(violations.size()).append("): "); for (ConstraintViolation<Object> violation : violations) { msg.append(violation.getMessage()).append(", "); } throw new ConstraintViolationException(msg.toString(), violations); } } }
我们可以如下使用它:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.DeserializationConfig; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.deser.BeanDeserializer; import com.fasterxml.jackson.databind.deser.BeanDeserializerBase; import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier; import com.fasterxml.jackson.databind.module.SimpleModule; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; import java.io.File; import java.io.IOException; import java.util.Set; public class JsonApp { public static void main(String[] args) throws Exception { File jsonFile = new File("./resource/test.json").getAbsoluteFile(); SimpleModule validationModule = new SimpleModule(); validationModule.setDeserializerModifier(new BeanDeserializerModifier() { @Override public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) { if (deserializer instanceof BeanDeserializer) { return new BeanValidationDeserializer((BeanDeserializer) deserializer); } return deserializer; } }); ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(validationModule); System.out.println(mapper.readValue(jsonFile, Pojo.class)); } } class Pojo { @NotNull @Size(min = 1, message = "Name should be at least 1 character.") private String name; @NotNull @Pattern(regexp = "^https://github.com/.+/.+$", message = "Link to github should match https://github.com/USER/REPOSITORY") private String github; // getters, setters, toString() }
For valid JSON payload:
{ "name": "Jackson", "github": "https://github.com/FasterXML/jackson-databind" }
prints:
Pojo{name='Jackson', github='https://github.com/FasterXML/jackson-databind'}
For invalid JSON payload:
{ "name": "", "github": "https://git-hub.com/FasterXML/jackson-databind" }
Exception in thread "main" javax.validation.ConstraintViolationException: JSON object is not valid. Reasons (2): Name should be at least 1 character., Link to github should match https://github.com/USER/REPOSITORY, at BeanValidationDeserializer.validate(JsonApp.java:110) at BeanValidationDeserializer.deserialize(JsonApp.java:97)