7

我将 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;
}

虽然我通常对这种消息模式很好,但实际上是 annyoing 是无法识别原始参数名称的事实,即我宁愿看到

登录。loginBody .password 而不是arg0

有没有一种简单的方法来解决这个问题,例如以某种方式为该参数提供一个明确的名称?

我正在使用 WildFly Swarm 2017.6.0。从我发现这意味着我有resteasy + resteasy-validator + hibernate-validator

谢谢。

4

4 回答 4

2

您可以尝试使用-parameters或指示您的 IDE 编译您的应用程序,例如在 eclipse 的情况下:首选项 - > java - > 编译器 - >“存储有关方法参数的信息(可通过反射使用)”

有了它,您就需要指示 Bean Validation 基础设施(例如)hibernate-validator 使用ReflectiveParameterNamervia META-INF/validation.xml

<parameter-name-provider>org.hibernate.validator.parameternameprovider.ReflectionParameterNameProvider</parameter-name-provider>

另请参阅Hibernate 验证器配置

我得到了可靠地使用Paranamer 库的东西

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-namerjboss-module 并从模块中引用该module.xml模块hibernate-validator

有了这个,我可以简单地写:

@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

于 2017-07-18T10:19:24.610 回答
1

有一个非常简单的解决方案:你可以在约束定义中设置自己的错误信息如下

@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

于 2018-06-20T12:44:00.223 回答
0

您是否可以尝试为 ConstraintViolationExceptions 实现异常映射器,并查看那里的信息(约束违规列表)是否可以帮助您获取参数名称?

于 2017-07-16T08:59:53.457 回答
0

Hibernate Validator 6.X的 @thomas-darimont 更新版本。

变体#1 - 在 Java 8 中构建(使用-parameters编译参数)

  1. 指定依赖项(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'
}
  1. 指定验证器:
@GET
@Path("user/{userId}")
public Response getUser(@Size(min = 2) @PathParam("userId") String userId) {
    return null;
}

注意:将返回从 Java 反射 API 获得的参数名称。 org.hibernate.validator.internal.engine.DefaultParameterNameProvider


变体 #2 - 使用 ParaNamer 库。(xml配置) 如果您不想依赖编译标志。

  1. 指定依赖项(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')
  1. 指定验证器:
@GET
@Path("user/{userId}")
public Response getUser(@Size(min = 2) @PathParam("userId") String userId) {
    return null;
}
  1. <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.ReflectionParameterNameProviderorg.hibernate.validator.parameternameprovider.ParanamerParameterNameProvider

问题:我可以仅使用 Java 代码样式进行配置吗? 很不幸的是,不行。(请参阅此处的详细信息)。

于 2018-11-16T15:31:20.630 回答