JavaBeans驗證(Bean驗證)是一種新的驗證模型,可作為Java EE 6平臺的一部分使用。 約束條件支持Bean驗證模型,該約束以注釋的形式出現在JavaBeans組件(例如托管Bean)的字段,方法或類上。
javax.validation.constraints
包中提供了一些內置約束。 Java EE 6教程列出了所有內置約束。
Bean驗證中的約束通過Java注釋表示:
public class Person {@NotNull@Size(min = 2, max = 50)private String name;// ...
}
Bean驗證和RESTful Web服務
JAX-RS 1.0為提取請求值并將其綁定到Java字段,屬性和參數(使用@HeaderParam
, @QueryParam
等注釋)提供了強大的支持。它還支持通過非注釋參數將請求實體主體綁定到Java對象中(也就是說,未使用任何JAX-RS注釋進行注釋的參數)。 當前,必須以編程方式對資源類中的這些值進行任何其他驗證。
下一個發行版JAX-RS 2.0包含一項建議,以使驗證批注可以與JAX-RS批注結合使用。 例如,給定驗證批注@Pattern
,以下示例顯示如何驗證表單參數。
@GET
@Path('{id}')
public Person getPerson(@PathParam('id')@Pattern(regexp = '[0-9]+', message = 'The id must be a valid number')String id) {return persons.get(id);
}
但是,目前,唯一的解決方案是使用專有實現。 接下來介紹的是基于JBoss的RESTEasy框架的解決方案,該解決方案符合JAX-RS規范,并通過注釋@ValidateRequest
添加了RESTful驗證接口。
導出的接口允許我們創建自己的實現。 但是,已經有一種廣泛使用的方法,RESTEasy還向其提供了無縫集成。 這個實現是Hibernate Validator 。 可以通過以下Maven依賴項將此提供程序添加到項目中:
<dependency><groupId>org.jboss.resteasy</groupId><artifactId>resteasy-jaxrs</artifactId><version>2.3.2.Final</version><scope>provided</scope>
</dependency>
<dependency><groupId>org.jboss.resteasy</groupId><artifactId>resteasy-hibernatevalidator-provider</artifactId><version>2.3.2.Final</version>
</dependency>
注意:在類或方法級別不聲明@ValidateRequest
, @ValidateRequest
在方法上應用了約束注釋,也不會進行驗證,例如上面的示例。
@GET
@Path('{id}')
@ValidateRequest
public Person getPerson(@PathParam('id')@Pattern(regexp = '[0-9]+', message = 'The id must be a valid number')String id) {return persons.get(id);
}
應用注釋后,發出請求時將自動驗證參數id
。
您當然可以通過使用注釋@Valid
來驗證整個實體,而不是單個字段。 例如,我們可以有一個接受Person
對象并對其進行驗證的方法。
@POST
@Path('/validate')
@ValidateRequest
public Response validate(@Valid Person person) {// ...
}
注意:
默認情況下,當驗證失敗時,容器將引發異常,并將HTTP 500狀態返回給客戶端。 可以/應該重寫此默認行為,使我們能夠自定義通過異常映射器返回給客戶端的Response。
國際化
到目前為止,我們一直在使用默認的或硬編碼的錯誤消息,但這既是一種不好的做法,又一點也不靈活。 I18n是Bean驗證規范的一部分,它使我們能夠使用資源屬性文件來指定自定義錯誤消息。 默認資源文件名稱為ValidationMessages.properties
并且必須包含屬性/值對,例如:
person.id.pattern=The person id must be a valid number
person.name.size=The person name must be between {min} and {max} chars long
注意:
{min}
和{max}
是指與消息相關聯的約束的屬性。
然后可以將這些已定義的消息注入驗證約束中,如下所示:
@POST
@Path('create')
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response createPerson(@FormParam('id')@Pattern(regexp = '[0-9]+', message = '{person.id.pattern}')String id,@FormParam('name')@Size(min = 2, max = 50, message = '{person.name.size}')String name) {Person person = new Person();person.setId(Integer.valueOf(id));person.setName(name);persons.put(Integer.valueOf(id), person);return Response.status(Response.Status.CREATED).entity(person).build();
}
要提供其他語言的翻譯,必須使用翻譯后的消息創建一個新的ValidationMessages_XX.properties
文件,其中XX
是所提供語言的代碼。
不幸的是,Hibernate Validator提供程序不基于特定的HTTP請求支持i18n。 它不考慮Accept-Language
HTTP標頭,并且始終使用Locale.getDefault()
提供的默認Locale
。 為了能夠改變Locale
使用Accept-Language
HTTP標頭(例如,改變語言在瀏覽器選項),必須提供自定義實現。
定制驗證器提供商
以下代碼旨在解決此問題,并已通過JBoss AS 7.1進行了測試。
首先要做的是刪除Maven resteasy-hibernatevalidator-provider
依賴性,因為我們提供了自己的提供程序,并添加了Hibernate Validator依賴性:
<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>4.2.0.Final</version>
</dependency>
接下來,創建一個自定義消息插值器以調整使用的默認Locale
。
public class LocaleAwareMessageInterpolator extendsResourceBundleMessageInterpolator {private Locale defaultLocale = Locale.getDefault();public void setDefaultLocale(Locale defaultLocale) {this.defaultLocale = defaultLocale;}@Overridepublic String interpolate(final String messageTemplate,final Context context) {return interpolate(messageTemplate, context, defaultLocale);}@Overridepublic String interpolate(final String messageTemplate,final Context context, final Locale locale) {return super.interpolate(messageTemplate, context, locale);}
}
下一步是提供ValidatorAdapter
。 引入此接口是為了將RESTEasy與實際的驗證API分離。
public class RESTValidatorAdapter implements ValidatorAdapter {private final Validator validator;private final MethodValidator methodValidator;private final LocaleAwareMessageInterpolator interpolator = new LocaleAwareMessageInterpolator();public RESTValidatorAdapter() {Configuration<?> configuration = Validation.byDefaultProvider().configure();this.validator = configuration.messageInterpolator(interpolator).buildValidatorFactory().getValidator();this.methodValidator = validator.unwrap(MethodValidator.class);}@Overridepublic void applyValidation(Object resource, Method invokedMethod,Object[] args) {// For the i8n to work, the first parameter of the method being validated must be a HttpHeadersif ((args != null) && (args[0] instanceof HttpHeaders)) {HttpHeaders headers = (HttpHeaders) args[0];List<Locale> acceptedLanguages = headers.getAcceptableLanguages();if ((acceptedLanguages != null) && (!acceptedLanguages.isEmpty())) {interpolator.setDefaultLocale(acceptedLanguages.get(0));}}ValidateRequest resourceValidateRequest = FindAnnotation.findAnnotation(invokedMethod.getDeclaringClass().getAnnotations(), ValidateRequest.class);if (resourceValidateRequest != null) {Set<ConstraintViolation<?>> constraintViolations = new HashSet<ConstraintViolation<?>>(validator.validate(resource,resourceValidateRequest.groups()));if (constraintViolations.size() > 0) {throw new ConstraintViolationException(constraintViolations);}}ValidateRequest methodValidateRequest = FindAnnotation.findAnnotation(invokedMethod.getAnnotations(), ValidateRequest.class);DoNotValidateRequest doNotValidateRequest = FindAnnotation.findAnnotation(invokedMethod.getAnnotations(),DoNotValidateRequest.class);if ((resourceValidateRequest != null || methodValidateRequest != null)&& doNotValidateRequest == null) {Set<Class<?>> set = new HashSet<Class<?>>();if (resourceValidateRequest != null) {for (Class<?> group : resourceValidateRequest.groups()) {set.add(group);}}if (methodValidateRequest != null) {for (Class<?> group : methodValidateRequest.groups()) {set.add(group);}}Set<MethodConstraintViolation<?>> constraintViolations = new HashSet<MethodConstraintViolation<?>>(methodValidator.validateAllParameters(resource,invokedMethod, args,set.toArray(new Class<?>[set.size()])));if (constraintViolations.size() > 0) {throw new MethodConstraintViolationException(constraintViolations);}}}
}
警告:
需要將@HttpHeaders
作為要驗證的方法的第一個參數注入:
@POST
@Path('create')
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response createPerson(@Context HttpHeaders headers,@FormParam('id')@Pattern(regexp = '[0-9]+', message = '{person.id.pattern}')String id,@FormParam('name')@Size(min = 2, max = 50, message = '{person.name.size}')String name) {Person person = new Person();person.setId(Integer.valueOf(id));person.setName(name);persons.put(id, person);return Response.status(Response.Status.CREATED).entity(person).build();
}
最后,創建將選擇以上用于驗證Bean驗證約束的類的提供程序:
@Provider
public class RESTValidatorContextResolver implementsContextResolver<ValidatorAdapter> {private static final RESTValidatorAdapter adapter = new RESTValidatorAdapter();@Overridepublic ValidatorAdapter getContext(Class<?> type) {return adapter;}
}
映射異常
Bean Validation API使用類型為javax.validation.ValidationException
或其任何子類的異常報告錯誤情況。 應用程序可以為任何異常提供自定義異常映射提供程序。 JAX-RS實現必須始終使用其泛型類型是異常的最接近超類的提供程序,應用程序定義的提供程序優先于內置提供程序。
異常映射器可能看起來像:
@Provider
public class ValidationExceptionMapper implementsExceptionMapper<MethodConstraintViolationException> {@Overridepublic Response toResponse(MethodConstraintViolationException ex) {Map<String, String> errors = new HashMap<String, String>();for (MethodConstraintViolation<?> methodConstraintViolation : ex.getConstraintViolations()) {errors.put(methodConstraintViolation.getParameterName(),methodConstraintViolation.getMessage());}return Response.status(Status.PRECONDITION_FAILED).entity(errors).build();}
}
上面的示例顯示了ExceptionMapper
的實現,該映射映射了MethodConstraintViolationException
類型的MethodConstraintViolationException
。 當用@ValidateRequest
注釋的方法的一個或多個參數的驗證失敗時,Hibernate Validator實現將引發此異常。 這樣可以確保客戶端收到格式化的響應,而不僅僅是從資源傳播的異常。
源代碼
這篇文章使用的源代碼可以在GitHub上找到 。
警告:
重命名資源屬性文件,以使文件ValidationMessages.properties
(即沒有任何后綴)可以映射到Locale.getDefault()
返回的Locale
。
相關文章
- Java和UTF-8編碼
- 作為JBoss AS 7模塊運行Drools 5.4.0 Final
- 比較設備描述存儲庫
- Java EE 6測試第二部分– Arquillian和ShrinkWrap簡介
- Java EE 6測試第I部分– EJB 3.1可嵌入API
- 上一篇文章:將Drools 5.4.0 Final作為JBoss AS 7模塊運行
參考:來自Samaxes博客的JCG合作伙伴 Samuel Santos的Java EE 6中的Bean驗證與JAX-RS集成 。
翻譯自: https://www.javacodegeeks.com/2013/01/integrating-bean-validation-with-jax-rs-in-java-ee-6.html