java jax-rs_在Java EE 6中将Bean验证与JAX-RS集成
java jax-rs
JavaBeans驗(yàn)證(Bean驗(yàn)證)是Java EE 6平臺的一部分提供的新驗(yàn)證模型。 約束通過以JavaBeans組件(例如托管Bean)的字段,方法或類上的注釋形式的約束來支持Bean驗(yàn)證模型。
javax.validation.constraints包中提供了幾個內(nèi)置約束。 Java EE 6教程列出了所有內(nèi)置約束。
Bean驗(yàn)證中的約束通過Java注釋表示:
Bean驗(yàn)證和RESTful Web服務(wù)
JAX-RS 1.0為提取請求值并將其綁定到Java字段,屬性和參數(shù)(使用@HeaderParam , @QueryParam等注釋)提供了強(qiáng)大的支持。它還支持通過非注釋參數(shù)將請求實(shí)體主體綁定到Java對象中(也就是說,未使用任何JAX-RS注釋進(jìn)行注釋的參數(shù))。 當(dāng)前,必須以編程方式對資源類中的這些值進(jìn)行任何其他驗(yàn)證。
下一個發(fā)行版JAX-RS 2.0包含一項(xiàng)建議,以使驗(yàn)證批注可以與JAX-RS批注結(jié)合使用。 例如,給定驗(yàn)證批注@Pattern ,以下示例顯示如何驗(yàn)證表單參數(shù)。
@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); }但是,目前,唯一的解決方案是使用專有實(shí)現(xiàn)。 接下來介紹的是基于JBoss的RESTEasy框架的解決方案,該解決方案符合JAX-RS規(guī)范,并通過注釋@ValidateRequest添加了RESTful驗(yàn)證接口。
導(dǎo)出的接口允許我們創(chuàng)建自己的實(shí)現(xiàn)。 但是,已經(jīng)有一種廣泛使用的方法,RESTEasy還向其提供了無縫集成。 這個實(shí)現(xiàn)是Hibernate Validator 。 可以通過以下Maven依賴項(xiàng)將此提供程序添加到項(xiàng)目中:
<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在方法上應(yīng)用了約束注釋,也不會進(jìn)行驗(yàn)證,例如上面的示例。
@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); } 應(yīng)用注釋后,發(fā)出請求時將自動驗(yàn)證參數(shù)id 。
您當(dāng)然可以通過使用注釋@Valid驗(yàn)證整個實(shí)體,而不是單個字段。 例如,我們可以有一個接受Person對象并對其進(jìn)行驗(yàn)證的方法。
注意:
默認(rèn)情況下,當(dāng)驗(yàn)證失敗時,容器將引發(fā)異常,并將HTTP 500狀態(tài)返回給客戶端。 可以/應(yīng)該重寫此默認(rèn)行為,使我們能夠自定義通過異常映射器返回給客戶端的Response。
國際化
到目前為止,我們一直在使用默認(rèn)的或硬編碼的錯誤消息,但這既是一種不好的做法,又一點(diǎn)也不靈活。 I18n是Bean驗(yàn)證規(guī)范的一部分,它使我們能夠使用資源屬性文件來指定自定義錯誤消息。 默認(rèn)資源文件名稱為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}是指與消息相關(guān)聯(lián)的約束的屬性。
然后可以將這些已定義的消息注入驗(yàn)證約束中,如下所示:
@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(); }要提供其他語言的翻譯,必須使用翻譯后的消息創(chuàng)建一個新的ValidationMessages_XX.properties文件,其中XX是所提供語言的代碼。
不幸的是,Hibernate Validator提供程序不基于特定的HTTP請求支持i18n。 它不考慮Accept-Language HTTP標(biāo)頭,并且始終使用Locale.getDefault()提供的默認(rèn)Locale 。 為了能夠改變Locale使用Accept-Language HTTP標(biāo)頭(例如,改變語言在瀏覽器選項(xiàng)),必須提供自定義實(shí)現(xiàn)。
定制驗(yàn)證器提供者
以下代碼旨在解決此問題,并已通過JBoss AS 7.1進(jìn)行了測試。
首先要做的是刪除Maven resteasy-hibernatevalidator-provider依賴性,因?yàn)槲覀兲峁┝俗约旱奶峁┏绦?#xff0c;并添加了Hibernate Validator依賴性:
<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>4.2.0.Final</version> </dependency>接下來,創(chuàng)建一個自定義消息插值器以調(diào)整使用的默認(rèn)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與實(shí)際的驗(yàn)證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作為要驗(yàn)證的方法的第一個參數(shù)注入:
@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(); }最后,創(chuàng)建將選擇以上用于驗(yàn)證Bean驗(yàn)證約束的類的提供程序:
@Provider public class RESTValidatorContextResolver implementsContextResolver<ValidatorAdapter> {private static final RESTValidatorAdapter adapter = new RESTValidatorAdapter();@Overridepublic ValidatorAdapter getContext(Class<?> type) {return adapter;} }映射異常
Bean驗(yàn)證API使用類型為javax.validation.ValidationException或其任何子類的異常報告錯誤情況。 應(yīng)用程序可以為任何異常提供自定義異常映射提供程序。 JAX-RS實(shí)現(xiàn)必須始終使用其泛型類型是異常的最接近超類的提供程序,其中應(yīng)用程序定義的提供程序優(yōu)先于內(nèi)置提供程序。
異常映射器可能看起來像:
@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的實(shí)現(xiàn),該映射映射了MethodConstraintViolationException類型的MethodConstraintViolationException 。 當(dāng)用@ValidateRequest注釋的方法的一個或多個參數(shù)的驗(yàn)證失敗時,Hibernate Validator實(shí)現(xiàn)將引發(fā)此異常。 這樣可以確保客戶端收到格式化的響應(yīng),而不僅僅是從資源傳播的異常。
源代碼
這篇文章使用的源代碼可以在GitHub上找到 。
警告:
重命名資源屬性文件,以使文件ValidationMessages.properties (即,沒有任何后綴)可以映射到Locale.getDefault()返回的Locale 。
相關(guān)文章
- Java和UTF-8編碼
- 作為JBoss AS 7模塊運(yùn)行Drools 5.4.0 Final
- 比較設(shè)備描述存儲庫
- Java EE 6測試第二部分– Arquillian和ShrinkWrap簡介
- Java EE 6測試第I部分– EJB 3.1可嵌入API
- 上一篇文章:作為JBoss AS 7模塊運(yùn)行Drools 5.4.0 Final
參考:來自Samaxes博客的JCG合作伙伴 Samuel Santos的Java EE 6中的Bean驗(yàn)證與JAX-RS集成 。
翻譯自: https://www.javacodegeeks.com/2013/01/integrating-bean-validation-with-jax-rs-in-java-ee-6.html
java jax-rs
總結(jié)
以上是生活随笔為你收集整理的java jax-rs_在Java EE 6中将Bean验证与JAX-RS集成的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 苹果手机拦截短信设置
- 下一篇: 畿是什么意思 畿具体是什么意思