使用 JAX-RS (rest-easy) 进行 Bean 验证:无法识别参数名称
Bean Validation with JAX-RS (rest-easy): parameter name not recognized
我正在将 JAX-RS 资源与 Bean Validation 结合使用,并按预期在这两个作品之间进行集成。
但是,在验证错误报告参数名称为 arg0 的情况下生成的默认错误消息,就像这样
[PARAMETER]
[login.arg0.password]
[password is required]
[]
对应方法定义:
@POST //and other JAX-RS annotations
public Response login(
@NotNull
@Valid
LoginBody loginBody) {
[...]
protected static class LoginBody {
@NotNull(message = EMAIL_REQUIRED)
public String email;
@NotNull(message = PASSWORD_REQUIRED)
public String password;
}
虽然我对这种消息模式大体上没什么意见,但真正令人讨厌的是原始参数名称无法识别的事实,即。 e.我更想看
login.loginBody.password 而不是 arg0.
有没有简单的方法来解决这个问题,例如。 G。以某种方式为该参数提供一个明确的名称?
我正在使用 WildFly Swarm 2017.6.0。据我发现,这意味着我有 resteasy + resteasy-validator + hibernate-validator
谢谢。
你能否尝试为 ConstraintViolationExceptions 实现一个异常映射器,看看你那里的信息(约束违规列表)是否可以帮助你获得参数名称?
您可以尝试使用 -parameters
编译您的应用程序或指示您的 IDE 这样做,例如的情况下
日食:首选项 -> java -> 编译器 -> "store information about method parameters (usable via reflection)"
有了这个,你就需要指示 Bean Validation 基础设施(例如)hibernate-validator 来
通过 META-INF/validation.xml
.
使用 ReflectiveParameterNamer
<parameter-name-provider>org.hibernate.validator.parameternameprovider.ReflectionParameterNameProvider</parameter-name-provider>
另见 Hibernate Validator Configuration
我得到了与 Paranamer library
可靠工作的东西
META-INF/validation.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<validation-config
xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://jboss.org/xml/ns/javax/validation/configuration
validation-configuration-1.1.xsd"
version="1.1">
<default-provider>org.hibernate.validator.HibernateValidator
</default-provider>
<message-interpolator>org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator
</message-interpolator>
<traversable-resolver>org.hibernate.validator.internal.engine.resolver.DefaultTraversableResolver
</traversable-resolver>
<constraint-validator-factory>org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl
</constraint-validator-factory>
<parameter-name-provider>org.hibernate.validator.parameternameprovider.ParanamerParameterNameProvider</parameter-name-provider>
</validation-config>
为了 paranamer
使用 wildfly,我需要创建一个 parameter-namer
jboss 模块
并从 hibernate-validator
模块的 module.xml
引用该模块。
有了这个我可以简单地写:
@POST
public Response login(@NotNull @Valid @Named("authRequest") AuthRequest authRequest) {
return Response.ok().build();
}
...
public class AuthRequest {
@NotNull(message = AuthMessages.EMAIL_REQUIRED)
public String email;
@NotNull(message = AuthMessages.PASSWORD_REQUIRED)
public String password;
}
对于通过 curl
发送的请求产生以下响应:
curl -H "Content-Type: application/json" -H "Accept: application/json" -d '{"email":"foo@bar.com"}' -v http://localhost:8080/javaweb-training/resources/auth
回复:
{"exception":null,"fieldViolations":[],"propertyViolations":[],"classViolations":[],"parameterViolations":[{"constraintType":"PARAMETER","path":"login.authRequest.password","message":"password.required","value":""}],"returnValueViolations":[]}%
...注意 login.authRequest.password
而不仅仅是 login.arg0.password
有一个很简单的解决方法:可以在约束定义中设置自己的错误信息如下
@NotNull(message = "password is required")
如果您想要一个基于 JAX-RS 参数注释的更通用的解决方案,您可以实现自己的简单 ParameterNamProvider
并将其注册到 validation.xml
中,如下所示。这样做的好处是不必更改 jboss 模块结构。我也不必更改任何编译器标志...
public class AnnotatedParameterNameProvider implements ParameterNameProvider {
@Override
public List<String> getParameterNames(Constructor<?> constructor) {
return lookupParameterNames(constructor.getParameterAnnotations());
}
@Override
public List<String> getParameterNames(Method method) {
return lookupParameterNames(method.getParameterAnnotations());
}
private List<String> lookupParameterNames(Annotation[][] annotations) {
final List<String> names = new ArrayList<>();
if (annotations != null) {
for (Annotation[] annotation : annotations) {
String annotationValue = null;
for (Annotation ann : annotation) {
annotationValue = getAnnotationValue(ann);
if (annotationValue != null) {
break;
}
}
// if no matching annotation, must be the request body
if (annotationValue == null) {
annotationValue = "requestBody";
}
names.add(annotationValue);
}
}
return names;
}
private static String getAnnotationValue(Annotation annotation) {
if (annotation instanceof HeaderParam) {
return ((HeaderParam) annotation).value();
} else if (annotation instanceof PathParam) {
return ((PathParam) annotation).value();
} else if (annotation instanceof QueryParam) {
return ((QueryParam) annotation).value();
}
return null;
}
}
在validation.xml中:
<validation-config xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/configuration validation-configuration-1.1.xsd"
version="1.1">
<parameter-name-provider>com.yourcompany.providers.AnnotatedParameterNameProvider</parameter-name-provider>
</validation-config>
请注意,您还可以通过实施自己的 MessageInterpolator
并将其注册到 validation.xml
来自定义错误消息的格式
Hibernate Validator 6.X.
的@thomas-darimont 更新版本
Variant#1 - 内置 Java 8(使用 -parameters
编译参数)
- 指定依赖项(gradle 示例):
// Define explicit hibernate validator 6.x
implementation('org.hibernate.validator:hibernate-validator:6.0.13.Final')
implementation('org.jboss.resteasy:resteasy-validator-provider-11:3.6.2.Final') {
// Exclude transitive hibernate validator 5.x
exclude group: 'org.hibernate', module: 'hibernate-validator'
}
- 指定验证者:
@GET
@Path("user/{userId}")
public Response getUser(@Size(min = 2) @PathParam("userId") String userId) {
return null;
}
注:org.hibernate.validator.internal.engine.DefaultParameterNameProvider
会return参数名从Java反射得到API。
变体 #2 - 使用 ParaNamer 库。 (xml配置)
如果您不想依赖编译标志。
- 指定依赖项(gradle 示例):
// Define explicit hibernate validator 6.x
implementation('org.hibernate.validator:hibernate-validator:6.0.13.Final')
implementation('org.jboss.resteasy:resteasy-validator-provider-11:3.6.2.Final') {
// Exclude transitive hibernate validator 5.x
exclude group: 'org.hibernate', module: 'hibernate-validator'
}
// ParaNamer library
implementation('com.thoughtworks.paranamer:paranamer:2.8')
- 指定验证者:
@GET
@Path("user/{userId}")
public Response getUser(@Size(min = 2) @PathParam("userId") String userId) {
return null;
}
- 放
<project_dir>/src/main/resources/META-INF/validation.xml
<?xml version="1.0" encoding="UTF-8"?>
<validation-config
xmlns="http://xmlns.jcp.org/xml/ns/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/validation/configuration
http://xmlns.jcp.org/xml/ns/validation/configuration/validation-configuration-2.0.xsd"
version="2.0">
<parameter-name-provider>org.hibernate.validator.parameternameprovider.ParanamerParameterNameProvider</parameter-name-provider>
</validation-config>
注意: 由于 Hibernate Validator 6.x org.hibernate.validator.parameternameprovider.ReflectionParameterNameProvider
已弃用,请改用 org.hibernate.validator.parameternameprovider.ParanamerParameterNameProvider
。
问题:我可以仅使用 Java 代码样式配置吗?
抱歉不行。 (See details here).
我正在将 JAX-RS 资源与 Bean Validation 结合使用,并按预期在这两个作品之间进行集成。
但是,在验证错误报告参数名称为 arg0 的情况下生成的默认错误消息,就像这样
[PARAMETER]
[login.arg0.password]
[password is required]
[]
对应方法定义:
@POST //and other JAX-RS annotations
public Response login(
@NotNull
@Valid
LoginBody loginBody) {
[...]
protected static class LoginBody {
@NotNull(message = EMAIL_REQUIRED)
public String email;
@NotNull(message = PASSWORD_REQUIRED)
public String password;
}
虽然我对这种消息模式大体上没什么意见,但真正令人讨厌的是原始参数名称无法识别的事实,即。 e.我更想看
login.loginBody.password 而不是 arg0.
有没有简单的方法来解决这个问题,例如。 G。以某种方式为该参数提供一个明确的名称?
我正在使用 WildFly Swarm 2017.6.0。据我发现,这意味着我有 resteasy + resteasy-validator + hibernate-validator
谢谢。
你能否尝试为 ConstraintViolationExceptions 实现一个异常映射器,看看你那里的信息(约束违规列表)是否可以帮助你获得参数名称?
您可以尝试使用 -parameters
编译您的应用程序或指示您的 IDE 这样做,例如的情况下
日食:首选项 -> java -> 编译器 -> "store information about method parameters (usable via reflection)"
有了这个,你就需要指示 Bean Validation 基础设施(例如)hibernate-validator 来
通过 META-INF/validation.xml
.
ReflectiveParameterNamer
<parameter-name-provider>org.hibernate.validator.parameternameprovider.ReflectionParameterNameProvider</parameter-name-provider>
另见 Hibernate Validator Configuration
我得到了与 Paranamer library
可靠工作的东西META-INF/validation.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<validation-config
xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://jboss.org/xml/ns/javax/validation/configuration
validation-configuration-1.1.xsd"
version="1.1">
<default-provider>org.hibernate.validator.HibernateValidator
</default-provider>
<message-interpolator>org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator
</message-interpolator>
<traversable-resolver>org.hibernate.validator.internal.engine.resolver.DefaultTraversableResolver
</traversable-resolver>
<constraint-validator-factory>org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl
</constraint-validator-factory>
<parameter-name-provider>org.hibernate.validator.parameternameprovider.ParanamerParameterNameProvider</parameter-name-provider>
</validation-config>
为了 paranamer
使用 wildfly,我需要创建一个 parameter-namer
jboss 模块
并从 hibernate-validator
模块的 module.xml
引用该模块。
有了这个我可以简单地写:
@POST
public Response login(@NotNull @Valid @Named("authRequest") AuthRequest authRequest) {
return Response.ok().build();
}
...
public class AuthRequest {
@NotNull(message = AuthMessages.EMAIL_REQUIRED)
public String email;
@NotNull(message = AuthMessages.PASSWORD_REQUIRED)
public String password;
}
对于通过 curl
发送的请求产生以下响应:
curl -H "Content-Type: application/json" -H "Accept: application/json" -d '{"email":"foo@bar.com"}' -v http://localhost:8080/javaweb-training/resources/auth
回复:
{"exception":null,"fieldViolations":[],"propertyViolations":[],"classViolations":[],"parameterViolations":[{"constraintType":"PARAMETER","path":"login.authRequest.password","message":"password.required","value":""}],"returnValueViolations":[]}%
...注意 login.authRequest.password
而不仅仅是 login.arg0.password
有一个很简单的解决方法:可以在约束定义中设置自己的错误信息如下
@NotNull(message = "password is required")
如果您想要一个基于 JAX-RS 参数注释的更通用的解决方案,您可以实现自己的简单 ParameterNamProvider
并将其注册到 validation.xml
中,如下所示。这样做的好处是不必更改 jboss 模块结构。我也不必更改任何编译器标志...
public class AnnotatedParameterNameProvider implements ParameterNameProvider {
@Override
public List<String> getParameterNames(Constructor<?> constructor) {
return lookupParameterNames(constructor.getParameterAnnotations());
}
@Override
public List<String> getParameterNames(Method method) {
return lookupParameterNames(method.getParameterAnnotations());
}
private List<String> lookupParameterNames(Annotation[][] annotations) {
final List<String> names = new ArrayList<>();
if (annotations != null) {
for (Annotation[] annotation : annotations) {
String annotationValue = null;
for (Annotation ann : annotation) {
annotationValue = getAnnotationValue(ann);
if (annotationValue != null) {
break;
}
}
// if no matching annotation, must be the request body
if (annotationValue == null) {
annotationValue = "requestBody";
}
names.add(annotationValue);
}
}
return names;
}
private static String getAnnotationValue(Annotation annotation) {
if (annotation instanceof HeaderParam) {
return ((HeaderParam) annotation).value();
} else if (annotation instanceof PathParam) {
return ((PathParam) annotation).value();
} else if (annotation instanceof QueryParam) {
return ((QueryParam) annotation).value();
}
return null;
}
}
在validation.xml中:
<validation-config xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/configuration validation-configuration-1.1.xsd"
version="1.1">
<parameter-name-provider>com.yourcompany.providers.AnnotatedParameterNameProvider</parameter-name-provider>
</validation-config>
请注意,您还可以通过实施自己的 MessageInterpolator
并将其注册到 validation.xml
Hibernate Validator 6.X.
的@thomas-darimont 更新版本Variant#1 - 内置 Java 8(使用 -parameters
编译参数)
- 指定依赖项(gradle 示例):
// Define explicit hibernate validator 6.x
implementation('org.hibernate.validator:hibernate-validator:6.0.13.Final')
implementation('org.jboss.resteasy:resteasy-validator-provider-11:3.6.2.Final') {
// Exclude transitive hibernate validator 5.x
exclude group: 'org.hibernate', module: 'hibernate-validator'
}
- 指定验证者:
@GET
@Path("user/{userId}")
public Response getUser(@Size(min = 2) @PathParam("userId") String userId) {
return null;
}
注:org.hibernate.validator.internal.engine.DefaultParameterNameProvider
会return参数名从Java反射得到API。
变体 #2 - 使用 ParaNamer 库。 (xml配置) 如果您不想依赖编译标志。
- 指定依赖项(gradle 示例):
// Define explicit hibernate validator 6.x
implementation('org.hibernate.validator:hibernate-validator:6.0.13.Final')
implementation('org.jboss.resteasy:resteasy-validator-provider-11:3.6.2.Final') {
// Exclude transitive hibernate validator 5.x
exclude group: 'org.hibernate', module: 'hibernate-validator'
}
// ParaNamer library
implementation('com.thoughtworks.paranamer:paranamer:2.8')
- 指定验证者:
@GET
@Path("user/{userId}")
public Response getUser(@Size(min = 2) @PathParam("userId") String userId) {
return null;
}
- 放
<project_dir>/src/main/resources/META-INF/validation.xml
<?xml version="1.0" encoding="UTF-8"?>
<validation-config
xmlns="http://xmlns.jcp.org/xml/ns/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/validation/configuration
http://xmlns.jcp.org/xml/ns/validation/configuration/validation-configuration-2.0.xsd"
version="2.0">
<parameter-name-provider>org.hibernate.validator.parameternameprovider.ParanamerParameterNameProvider</parameter-name-provider>
</validation-config>
注意: 由于 Hibernate Validator 6.x org.hibernate.validator.parameternameprovider.ReflectionParameterNameProvider
已弃用,请改用 org.hibernate.validator.parameternameprovider.ParanamerParameterNameProvider
。
问题:我可以仅使用 Java 代码样式配置吗? 抱歉不行。 (See details here).