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对象并对其进行验证的方法。

@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 。 为了能够使用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

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

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

相关文章

CentOS 7 Flannel的安装与配置

1. 安装前的准备 etcd 3.2.9 Docker 17.12.0-ce 三台机器10.100.97.236, 10.100.97.92, 10.100.97.81 etcd不同版本之间的差别还是挺大的&#xff0c;使用V3版本跟Flannel整合起来会有坑&#xff0c;下文详解。 2. 安装 sudo yum install -y flannel 安装后&#xff0c;版本是0…

给Ambari集群里安装基于Hive的大数据实时分析查询引擎工具Impala步骤(图文详解)...

不多说&#xff0c;直接上干货&#xff01; Impala和Hive的关系&#xff08;详解&#xff09; 扩展博客 给Clouderamanager集群里安装基于Hive的大数据实时分析查询引擎工具Impala步骤&#xff08;图文详解&#xff09; 参考 hortonworks ambari集成impala ambari hdp 集成 imp…

python调用ffmpeg合并_用ffmpeg命令处理mp4剪切与合并

1. 剪切&#xff1a;./ffmpeg -ss 00:00:06 -t 00:00:12 -i input.mp4 -vcodec copy -acodec copy output.mp4意思是从截取从6秒开始&#xff0c;时长为12秒的视频&#xff0c;格式不变&#xff0c;输入为input.mp4输出为output.mp4-vcodec copy -acodec copy : 编码格式不变2.…

html中常见的小问题(1)

问题&#xff1a;自适应高度的块级元素内添加图片后&#xff0c;其高度会比图片高度多出一块 简单代码如下&#xff1a; <!doctype html> <html><head><style>.box{width:533px;margin:100px auto;border:1px solid red;}</style></head>…

Java程序员的10个XML面试问答

XML面试问题在各种编程工作面试中非常受欢迎&#xff0c;包括针对Web开发人员的Java面试 。 XML是一项成熟的技术&#xff0c;通常用作从一个平台传输数据的标准。 XML面试问题包含来自各种XML技术的问题&#xff0c;例如XSLT&#xff0c;该技术用于转换XML文件&#xff0c; XP…

[转] Java, 使用 Reactor 进行反应式编程

https://www.ibm.com/developerworks/cn/java/j-cn-with-reactor-response-encode/index.html?lnkhmhm转载于:https://www.cnblogs.com/pekkle/p/8311749.html

JmeterAnt构建自动化测试平台

一、jmeter jmeter下载地址为&#xff1a;http://jmeter.apache.org/download_jmeter.cgi 下载完成后&#xff0c;解压文件&#xff0c; 加压后&#xff0c;到biin目录下&#xff0c;点击jmeter.bat启动jmeter(如果是linux环境&#xff0c;给jmeter.sh可执行的权限&#xff0c;…

input复选框checkbox默认样式纯css修改

修改之前的样式 修改之后的样式 html <input type"checkbox" name"btn" id"btn1"><label for"btn1">按钮1</label> css input[type"checkbox"]{width:20px;height:20px;display: inline-block;text-al…

c++word书签_「职场必备」干货!WORD办公软件快捷键,小编整理拿走不谢

小编工作时的照片&#xff0c;不上镜CtrlShiftSpacebar创建不间断空格Ctrl -(连字符)创建不间断连字符CtrlB使字符变为粗体CtrlI使字符变为斜体CtrlU为字符添加下划线CtrlShift缩小字号CtrlShift>增大字号CtrlQ删除段落格式CtrlSpacebar删除字符格式CtrlC复制所选文本或对象…

struts.xml 配置

1.ActionSupport是默认的Action类,若某个action节点没有配置class属性&#xff0c;则ActionSupport即为待执行的Action类 2.在手工完成字段验证&#xff0c;显示错误消息&#xff0c;国际化等情况下&#xff0c;推荐继承ActionSupport 3.result是action节点的子节点&#xff0c…

前端学习---css基本知识

css基本知识 我们先看一个小例子&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body><div style"background-color:#2459a2;height:…

mysql 从库_mysql数据库主从配置

在一篇文章《离线安装mysql数据库》,讲解了离线安装mysql数据库的过程&#xff0c;本文将讲解mysql数据库的主从配置方法。mysql数据库进行主从配置后&#xff0c;可以实现数据库的备份、同时应用也可以实现读写分离&#xff0c;提高应用的并发量。1、主从原理从《高性能mysql》…

Java内联虚拟方法调用的性能

总览 动态编译的好处之一是&#xff0c;它能够支持在虚拟方法代码上进行广泛的方法内联。 虽然内联代码可以提高性能&#xff0c;但是代码仍然必须检查类型&#xff08;以防由于优化而更改了类型&#xff09;或在多个可能的实现之间进行选择。 这导致了问题。 通过接口调用的…

.Net Core 部署到 CentOS7 64 位系统中的步骤

建议使用 root 管理员账户操作 1、安装工具 1、apache 2、.Net Core(dotnet-sdk-2.0) 3、Supervisor(进程管理工具&#xff0c;目的是服务器一开机就启动服务器 上发布的ASP.NET Core Web网站) 2、安装apache 在安装apache之前&#xff0c;要先做一件事就是&#xff0c;把当前登…

【模板小程序】 十进制大数相乘(正数、负数、0均可),包含合法性检查

1 /*2 本程序说明&#xff1a;3 4 大数乘法(模拟乘法操作&#xff0c;取其中一个字符串&#xff0c;每一位分别相乘&#xff0c;最后移位加起来)5 6 时间复杂度&#xff1a;O(k1*k2),k1和k2分别为两字符串长度7 空间复杂度&#xff1a;O(1)8 9 */10 11 #include <iostream&g…

mysql更新字符串中某个字符串_mysql更新某个字符串字段的部分内容

如果现在需要Mysql更新字段重部分数据&#xff0c;而不是全部数据&#xff0c;应该采用何种方法呢&#xff1f;下面介绍了两种情况下Mysql更新字段中部分数据的方法&#xff0c;供您参考。Mysql更新字段中部分数据第一种情况&#xff1a;update tab set A concat(substr…

前端学习---html基础知识

HTML基本知识 学习html首先我们先看看HTML本质&#xff1a; web框架本质 我们在学socket&#xff0c;我们创建一个socketserver&#xff0c;然后运行起来&#xff0c;有一个client客户端要连接socket服务端&#xff0c;连接上之后&#xff0c;如果两边都没有close&#xff0…

Java 8最新消息

Java 8的开发再次开始主导新闻。 最近的帖子涵盖了扩展JDK 8的Milestone 7以确保其功能完整&#xff0c;Java 8中现在可用的Date / Time API以及对Java教程的更新以涵盖一些Java 8功能。 扩展JDK 8 M7 马克雷因霍尔德 &#xff08; Mark Reinhold &#xff09;以JDK 8 M6的身份…

插入排序-Java

1.算法描述 假定n是数组的长度&#xff0c; 首先假设第一个元素被放置在正确的位置上&#xff0c;这样仅需从1-n-1范围内对剩余元素进行排序。对于每次遍历&#xff0c;从0-i-1范围内的元素已经被排好序&#xff0c; 每次遍历的任务是&#xff1a;通过扫描前面已排序的子列表&a…

更改mysql数据库存放位置_更改mysql数据库存放位置

由于要做一个数据库的测试,所以选了台虚拟机进行,无奈硬盘分的太小,数据太大. 只能把数据放到新的硬盘上.所以要更改数据库的存储位置.1.新添加块硬盘,分区,挂载到/mysql下,新建lib文件夹.1.1 copy数据库文件, cp -Rp /var/lib/* /mysql/lib/1.2 修改lib权限为mysql. sudo cho…