jax-rs jax-ws_JAX-RS Bean验证错误消息国际化

jax-rs jax-ws

Bean验证简介

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 。 为了能够使用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验证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 Advent Calendar博客上我们JCG合作伙伴 Samuel Santos的JAX-RS Bean验证错误消息国际化 。

翻译自: https://www.javacodegeeks.com/2012/12/jax-rs-bean-validation-error-message-internationalization.html

jax-rs jax-ws

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

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

相关文章

补码和原码的转化过程

在计算机系统中&#xff0c;数值一律用补码来表示&#xff08;存储&#xff09;。 主要原因&#xff1a; 使用补码&#xff0c;可以将符号位和其它位统一处理&#xff1b;同时&#xff0c;减法也可按加法来处理。另外&#xff0c;两个用补 码表示的数相加时&#xff0c;如果最高…

Flask实现群聊

后端 from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer from geventwebsocket.websocket import WebSocket from flask import Flask,request,render_template user_socket_list [] app Flask(__name__)app.route("/conn_ws…

第一单元总结:基于基础语言、继承和接口的简单OOP

前情提要 到目前为止&#xff0c;OO课程已经完成了前三次的作业&#xff0c;分别为&#xff1a; 第一次作业&#xff1a;简单多项式的构造和求导。【正则表达式】【数据结构】【排序】第二次作业&#xff1a;含三角函数因子的复杂多项式的构造、求导和化简。【递归下降】【DFS】…

JUnit与TestNG:您应该选择哪种测试框架?

JUnit与TestNG&#xff1a;测试框架对决 在平衡良好的开发人员团队中&#xff0c;测试是软件发布周期中不可分割的一部分。 并非总是那样。 单元测试&#xff0c;集成测试&#xff0c;系统测试等并不总是存在的。 如今&#xff0c;我们很幸运能及时到达一个测试很重要且其价值…

小尾数,大尾数

大尾小尾 是数据在存储器中的存储格式&#xff0c;INtel采用的是小尾表示&#xff0c;即数据的高位存储在存储器的高地址&#xff0c;低位存储在存储器的低地址&#xff0c;例如一个十六进制数据0x1234存储在内存中&#xff0c;那么该数据在内存中的存储格式为&#xff1a; 34 …

s11.1 lsof:查看进程打开的文件

功能说明 lsof 全名为list open files&#xff0c;也就是列举系统中已经被打开的文件&#xff0c;通过lsof命令&#xff0c;就可以根据文件找到对应的进程信息&#xff0c;也可以根据进程信息找到进程打开的文件。【语法格式】 lsof [option]lsof [选项]参数选项 …

理解linux time命令的输出

Linux中time命令&#xff0c;我们经常用来计算 某个程序的运行耗时(real)&#xff0c; 用户态cpu耗时(user)&#xff0c; 系统态cpu耗时(sys)。例如&#xff1a;$ time foo real 0m0.003s user 0m0.000s sys 0m0.004s$ 那么这三个时间都具体代表什么意思…

matlab persistent静态变量

persistent关键字说明&#xff1b; matlab中persistent定义为一个静态变量&#xff0c;程序结束静态变量内存不释放&#xff0c;只有在matlab程序关闭时&#xff0c;静态变量的内存才被清除。当程序结束时&#xff0c;不希望变量的值被清空&#xff0c;这时可以用persistent关键…

小程序 转义_为内存密集型应用程序转义JVM堆

小程序 转义如果您曾经分配过大型Java堆&#xff0c;您就会知道在某个时候&#xff08;通常从大约4 GiB开始&#xff09;&#xff0c;您将开始遇到垃圾回收暂停的问题。 我不会详细说明为什么在JVM中会出现暂停&#xff0c;但是总之&#xff0c;当JVM进行完整的收集并且您有很…

Ubuntu 进入单用户模式—修改启动项利器

偶尔会遇到Ubuntu无法正常启动的情况&#xff0c;这时候需修改某些文件让系统正常启动&#xff0c;如果直接进入 recovery 模式&#xff0c;默认是文件权限只读&#xff0c;无法修改文件。这是我们需要进入recovery 的单用户模式&#xff0c;获得修改文件的权限。 1、重启ubunt…

博客教程中百度网盘地址

博客中百度网盘地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1ncGK5fXMBmmkuZGbBSohBw 提取码&#xff1a;v67x 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 转载于:https://www.cnblogs.com/PIESat/p/10593654.html

只有经验丰富的开发人员才能教您有关Java的5件事

深入研究Java之前需要了解的所有内容的概述 有许多工具&#xff0c;方法&#xff0c;环境和功能会改变您处理代码的方式&#xff0c;而这些通常是在学年期间不会遇到的。 尽管它在Java开发世界中迈出了第一步&#xff0c;但大多数实际学习都是在工作中进行的。 在接下来的文章…

conda命令

创建环境 conda create --name snowflake biopython 这条命令将会给biopython包创建一个新的环境&#xff0c;位置在/envs/snowflakes 很多跟在--后边常用的命令选项&#xff0c;可以被略写为一个短线加命令首字母。所以--name选项和-n的作用是一样的。通过conda -h或conda –-…

bzoj4443:[Scoi2015]小凸玩矩阵

传送门 二分答案是显然的啊&#xff0c;然后对于比二分出的值大的直接跑最大匹配&#xff0c;然后判定就好了 代码&#xff1a; #include<cstdio> #include<iostream> #include<algorithm> #include<vector> #include<cstring> using namespace …

GDB调试及其调试脚本的使用

一、GDB调试 1.1. GDB 概述 GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具。或许&#xff0c;各位比较喜欢那种图形界面方式的&#xff0c;像VC、BCB等IDE的调试&#xff0c;但如果你是在UNIX/Linux平台下做软件&#xff0c;你会发现GDB这个调试工具有比VC、BCB的图形…

Python中xlrd模块解析

xlrd 导入模块 import xlrd 2、打开指定的excel文件&#xff0c;返回一个data对象 data xlrd.open_workbook(file) #打开excel表&#xff0c;返回data对象 3、通过data对象可以得到各个sheet对象(一个excel文件可以有多个sheet&#xff0c;每个sheet就是一…

数学中常见的距离

1. 欧氏距离 最常见的两点之间或多点之间的距离表示法&#xff0c;又称之为欧几里得度量&#xff0c;它定义于欧几里得空间中&#xff0c;如点 x (x1,...,xn) 和 y (y1,...,yn) 之间的距离为&#xff1a; (1) 二维平面上两点a(x1,y1)与b(x2,y2)间的欧氏距离&#xff1a; (…

使用Spring Boot和Heroku在20分钟内完成Java的单点登录

建筑物身份管理&#xff0c;包括身份验证和授权&#xff1f; 尝试Stormpath&#xff01; 我们的REST API和强大的Java SDK支持可以消除您的安全风险&#xff0c;并且可以在几分钟内实现。 注册 &#xff0c;再也不会建立auth了&#xff01; 大规模更新 我最初为本文撰写的代码…