java jax-rs_在Java EE 6中将Bean验证与JAX-RS集成

java jax-rs

JavaBeans验证(Bean验证)是Java EE 6平台的一部分提供的新验证模型。 约束通过以JavaBeans组件(例如托管Bean)的字段,方法或类上的注释形式的约束来支持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验证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
  • 上一篇文章:作为JBoss AS 7模块运行Drools 5.4.0 Final

参考:来自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

java jax-rs

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/349698.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu16.04+cuda9.0_cudnn7.5+tensorflow-gpu==1.12.0

1、查找可用的tensorflow源&#xff0c;该命令运行后终端会输出所有可用的源 anaconda search -t conda tensorflow2、这里name是上一步中输出源的tensorflow name栏的名称&#xff0c;show命令会在终端输出该源具体的信息和下载需要的指令,执行后如图&#xff1a; anaconda sh…

介绍OpenHub框架

本文介绍了OpenHub框架 -基于Apache Camel的新的开源集成解决方案。 本文回答了一些问题&#xff0c;为什么您应该关心另一个集成框架&#xff0c;强弱属性以及如何使用OpenHub启动新项目。 OpenHub框架是Apache Camel&#xff0c;但经过改进…… 当然&#xff0c;您只能使用A…

问题 1072: 汽水瓶

题目描述有这样一道智力题&#xff1a;“某商店规定&#xff1a;三个空汽水瓶可以换一瓶汽水。小张手上有十个空汽水瓶&#xff0c;她最多可以换多少瓶汽水喝&#xff1f;”答案是5瓶&#xff0c;方法如下&#xff1a;先用9个空瓶子换3瓶汽水&#xff0c;喝掉3瓶满的&#xff0…

呼叫我或异步REST

本文是使用Spring Boot Java 8创建的工作正常的异步REST应用程序的非常简单的示例。SpringBoot使得开发Web应用程序几乎非常容易&#xff0c;但是为了简化任务&#xff0c;我从Spring存储库中举了一个例子&#xff0c;称为rest- service &#xff0c;将其分叉到我自己的存储库…

NOIP模拟测试22「位运算」

范围n-----$100000$ m $30$ 输出方案 这是一个很好的$dp$题 首先我们应该看出来一条性质只要你最后有方案达到$n$个$1$&#xff0c;那么你可以达到任何一种$n$个$1$的情况 例如 你最后可以达到$3$个$1$ 那么你可以达到$11100 $ $ 01110$ $01011$ $01101$等方案 证明&a…

系统测试集成测试单元测试_等待正确的时刻–集成测试

系统测试集成测试单元测试当您必须测试多线程程序时&#xff0c;总是需要等到系统达到特定状态为止&#xff0c;此时测试可以验证是否已达到正确的状态。 这样做的通常方法是在系统中插入一个“探针”&#xff0c;该探针将向同步原语发出信号 &#xff08;例如Semaphore &…

Apollo的基本概念和集成实战

基本概念 使用场景 是一个分布式的配置中心。适用于微服务&#xff1b; 核心功能 集中管理不同环境&#xff0c;不同集群的配置&#xff1b;配置修改后可以实时推送到应用端&#xff1b;具备规范的权限&#xff0c;流程治理特性&#xff1b;开发技术 服务端使用springboot,spri…

flatMap()和事件顺序– RxJava常见问题解答

正如我们已经发现的&#xff0c; flatMap()不会保留原始流的顺序。 让我们使用上一篇文章的GeoNames API示例进行说明 &#xff1a; public interface GeoNames {Flowable<Long> populationOf(String city);}通过使用flatMap()请求多个城市的人口&#xff0c;我们不能保…

技术管理规划-从哪入手?

前言 最近在学习技术管理课程&#xff0c;对学习的内容做一些笔记&#xff0c;结合当前的工作环境在对应的知识点上做一些思考&#xff0c;慢慢建立自己的团队管理方法论。 学习方法 早上骑单车的时候或者跑步的时候听音频&#xff0c;熟悉课程内容&#xff1b;仔细阅读文字&am…

NOIP模拟测试23「mine·water·gcd」

mine 题解 一道比较水的dp 考试因为初始化挂掉了只有$80$分 代码有注释 #include<bits/stdc.h> using namespace std; //无脑dp #define ll long long #define A 2222222 const ll mod1e97; //设f[x][5][2]为当前是第x个格子,当前填什么 0前面填1后面填 ll f[A][5][2]; /…

在AWS中设置Cassandra集群

Apache Cassandra是一个NoSQL数据库&#xff0c;它使用一致的哈希机制可以轻松进行水平扩展。 七年前&#xff0c;我尝试了它&#xff0c;并决定不将其用于我的附带项目&#xff0c;因为它太新了。 现在情况有所不同&#xff0c;Cassandra早已建立起来&#xff0c;背后有一家公…

k8s停止服务_使用 K8S 几年后,这些技术专家有话要说

9 月 7 日下午&#xff0c;在深圳南山软件产业基地&#xff0c;腾讯云 K8S & 云原生技术开放日成功落幕&#xff0c;来自腾讯、灵雀云、超参数科技、虎牙等资深技术专家与现场开发者共同探讨企业落地 K8S 的过程中遇到的难点以及解决问题的方法。K8S 逐渐成为容器编排的标准…

spring的事件机制实战

理论 在分布式场景下&#xff0c;实现同步转异步的方式有三种方式&#xff1a; 1.异步线程池执行&#xff1b;比如借助Asyn注解&#xff0c;放到spring自带的线程池中去执行&#xff1b; 2.放到消息队列中&#xff0c;在消费者的代码中异步的消费&#xff0c;执行相关的逻辑&am…

苹果wifi网速慢怎么办_所以,WiFi和4G到底哪个更耗电?

来源 | 中科院物理所(ID&#xff1a;cas-iop)编辑 | 椒盐猫巨烦现代人行走江湖&#xff0c;必备三件法宝&#xff1a;手机&#xff0c;网络&#xff0c;充电宝。即便在4G基站遍布各个旮旮角角的今天&#xff0c;当你带着心仪的人儿走进一家咖啡店&#xff0c;第一件事仍然是低声…

问题 1073: 弟弟的作业

题目描述你的弟弟刚做完了“100以内数的加减法”这部分的作业&#xff0c;请你帮他检查一下。每道题目&#xff08;包括弟弟的答案&#xff09;的格式为abc或者a-bc&#xff0c;其中a和b是作业中给出的&#xff0c;均为不超过100的非负整数&#xff1b;c是弟弟算出的答案&#…

苹果4怎么越狱_苹果手机如何截屏

很多用过苹果手机的用户对苹果的评价可是非常高对&#xff0c;苹果手机最大的特点便是手感好&#xff0c;并且使用一年以上也不会卡机现象&#xff0c;因此苹果手机才得到了广泛的关注。苹果手机的截屏程序与安卓手机不同&#xff0c;一般情况下&#xff0c;安卓手机是可以设置…

简单的Spring Boot管理员设置

Spring Boot Admin是一个不错的仪表板&#xff0c;用于监视您的Spring Boot应用程序。 但是&#xff0c;设置它并不是一件容易的事。 该文档概述了两个选项 &#xff1a; 在连接到admin应用程序的启动应用程序中包括一个客户端库–这要求将admin应用程序部署在公共位置或至少可…

NOIP模拟测试19「count·dinner·chess」

反思: 我考得最炸的一次 怎么说呢?简单的两个题0分,稍难(我还不敢说难,肯定又有人喷我)42分 前10分钟看T1,不会,觉得不可做,完全不可做,把它跳了 最后10分钟看T1,发现一个有点用的性质,仍然认为不可实现 0分 所以T1是什么样的难题呢 即使暴力也有60分,但我楞没想出来暴力怎么打…

react组件卸载调用的方法_好程序员web前端培训分享React学习笔记(三)

好程序员web前端培训分享React学习笔记(三)&#xff0c;组件的生命周期React中组件也有生命周期&#xff0c;也就是说也有很多钩子函数供我们使用, 组件的生命周期&#xff0c;我们会分为四个阶段&#xff0c;初始化、运行中、销毁、错误处理(16.3之后)初始化在组件初始化阶段会…

静态工厂方法代替构造器实例_静态工厂方法与传统构造方法

静态工厂方法代替构造器实例之前&#xff0c;我已经讨论过一些关于Builder模式的信息 &#xff0c; Builder Pattern是一种有用的模式&#xff0c;用于实例化具有几个&#xff08;可能是可选的&#xff09;属性的类&#xff0c;这些属性可以使读取&#xff0c;编写和维护客户端…