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" } ] }
- 如何使用Dropwizard 1.0.2中的LoggingFeature打印服务器响应?
- Dropwizard不会将自定义记录器记录到文件中
- 不使用.YML文件的Dropwizard配置?
- 使用Jersey的AbstractHttpContextInjectable自定义方法注释不工作
- 基于没有单例的HttpRequest的jersey 2上下文注入
- Jersey – 有没有办法用参数实例化Per-Request资源?
- @Context注入无效在Jersey ContainerRequestFilter(Dropwizard)
- DropWizard Auth by Example
- 如何在Dropwizard中进行资源的基本身份validation