JAX-RS Bean验证错误消息国际化
Bean驗證簡介
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對象并對其進行驗證的方法。
注意:
默認情況下,當驗證失敗時,容器將引發異常,并將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 。 為了能夠使用Accept-Language HTTP標頭更改Locale ,必須提供一個自定義實現。
定制驗證器提供商
以下代碼旨在解決此問題,并已通過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 。
作者:Samuel Santos是Java和開放源代碼傳播者,也是PT.JUG(葡萄牙Java用戶組)的JUG負責人。 他是葡萄牙科英布拉市Present Technologies的技術主管,負責刺激創新,知識共享,教練和技術選擇活動。 塞繆爾(Samuel)是博客samaxes.com的作者,并以@samaxes鳴叫。
參考: Java出現日歷博客中來自JCG合作伙伴 Samuel Santos的JAX-RS Bean驗證錯誤消息國際化 。
翻譯自: https://www.javacodegeeks.com/2012/12/jax-rs-bean-validation-error-message-internationalization.html
總結
以上是生活随笔為你收集整理的JAX-RS Bean验证错误消息国际化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 跑步机设置(跑步机设置多少)
- 下一篇: iPhone 7S/7SP/8已全面开始