DropWizard中的Beanvalidation与自定义Json输出

我开发了一个具有各种Restfunction的web服务。 我想使用标准的@Valid注释来validation我的bean。 但我想修改输出json错误。

来自validation的错误消息目前的格式如下:

 { "errors": [ "someString size must be between 0 and 140", "anotherString cannot contain numbers" ] } 

但我希望错误消息的格式如下:

 { "errors": [{ "someString": "size must be between 0 and 140" }, { "anotherString": "cannot contain numbers" } ] } 

要么

 { "errors": [{ "field": "someString" "error": "size must be between 0 and 140" }, { "field": "anotherString" "error": "cannot contain numbers" } ] } 

我知道如何通过向validation注释提供message="some message about strings"来改变消息的错误,甚至使用ValidationMEssages.properties作为所有错误消息的公共位置。 但是,如果发生错误,我无法改变输出格式。

我已阅读以下文档,但我需要更多指导。 http://www.dropwizard.io/1.0.0/docs/manual/validation.html

这是我的第一个DropWizard项目,我习惯于在Spring中开发。

提前致谢。

我找到了解决自己问题的方法。 我决定发布它,如果有人应该像我一样有问题。

这是针对DropWizard 1.0,我没有测试它是否适用于更早版本/更高版本,所以请记住这一点。 我无法为您提供完整的解决方案,但我已将我的解决方案作为代码片段发布,因此不要指望您可以简单地复制/粘贴和编译。

解决方案实际上很简单,您只需要为ConstraintViolationException实现自己的ExceptionMapper并使用DropWizard重新编译它。

您可以通过提供模板或常规文本轻松地为beanvalidation定义自己的消息。 即

@NotNull(message="God damn it, Morty. I ask you to do one thing!")

要么

@NotNull(message="{morty.error}")

模板位于ValidationMessages.properties ,您必须自己创建并放在src/main/resources/

ValidationMessages.properties

 morty.error=God damn it, Morty. I ask you to do one thing! 

无论如何,这是我的解决方案。

SomeApplication.class

 //Class where you setup DropWizard public class SomeApplication extends Application { @Override public void run(SomeConfiguration conf, Environment environment) throws Exception { //Remove all default ExceptionMappers ((DefaultServerFactory)conf.getServerFactory()).setRegisterDefaultExceptionMappers(false); //Register your custom ExceptionMapper for ConstraintViolationException environment.jersey().register(new CustomExceptionMapper()); //Restore the default ExceptionsMappers that you just removed environment.jersey().register(new LoggingExceptionMapper() {}); environment.jersey().register(new JsonProcessingExceptionMapper()); environment.jersey().register(new EarlyEofExceptionMapper()); } } 

CustomExceptionMapper.class

 //This is where the magic happens. //This is your custom ExceptionMapper for ConstraintViolationException @Provider public class CustomExceptionMapper implements ExceptionMapper { @Override public Response toResponse(ConstraintViolationException cve) { //List to store all the exceptions that you whish to output //ValidationErrors is a custom object, which you can see further down ValidationErrors validationErrors = new ValidationErrors(); //Loop through all the ConstraintViolations for(ConstraintViolation c : cve.getConstraintViolations()){ //We retrieve the variable name or method name where the annotation was called from. //This will be your property name for your JSON output. String field = ((PathImpl)c.getPropertyPath()).getLeafNode().getName(); //If field is null, then the notation is probably at a class level. //Set field to class name if(field == null){ field = c.getLeafBean().getClass().getSimpleName(); } //c.getMessage() is the error message for your annotation. validationErrors.add(field, c.getMessage()); } //Return a response with proper http status. return Response.status(422).entity(validationErrors).build(); } } 

ValidationErrors

 //There is not really any magic happening here. //This class is just a wrapper for a List with the type ValidationObject. public class ValidationErrors { public List errors = new ArrayList(); public void add(String field, String error){ errors.add(new ValidationObject(field, error)); } } 

ValidationObject.class

 //Once again, no magic public class ValidationObject { //This will be your property names private String field, error; public ValidationObject(String field, String error){ this.field = field; this.error = error; } public String getField() { return field; } public void setField(String field) { this.field = field; } public String getError() { return error; } public void setError(String error) { this.error = error; } } 

TestClass.class

 //This is just a class to showcase the functionality //I have not posted any codesnippers for this @CustomClassConstaint, //it is a custom annotaiton. //I only included this annotation to show how //the CustomExceptionMapper handles annotations on a class level @CustomClassConstaint public class TestClass { @NotNull @Size(min = 2, max = 5) public String testString1; @NotNull @Size(min = 2, max = 5) public String testString2; @Min(10) @Max(20) public int testInt1; @Min(10) @Max(20) public int testInt2; } 

restfunction进行测试

  //Some rest function to showcase @POST @Path("/path/to/test") //Remember @Valid or annotations will not be validated public Response callRestTestMethod(@Valid TestClass testObject){ return Response.ok().build(); } 

输入测试:

 POST /path/to/test { "testString1": null, "testString2": "", "testInt1": 9, "testInt2": 21 } 

测试输出:

订单有点随机,每次调用callRestTestMethod(...)更改。 validation是逐个触发的,因为它们在组件树中,我不知道是否可以控制订单。

 { "errors": [ { "field": "TestClass", "error": "custom error msg" }, { "field": "testInt1", "error": "must be greater than or equal to 10" }, { "field": "testString1", "error": "may not be null" }, { "field": "testInt2", "error": "must be less than or equal to 20" }, { "field": "testString2", "error": "size must be between 2 and 5" } ] }