【问题标题】:Bean Validation with JAX-RS (rest-easy): parameter name not recognized使用 JAX-RS (rest-easy) 进行 Bean 验证:参数名称无法识别
【发布时间】:2023-03-27 01:30:02
【问题描述】:

我正在使用 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 是原始参数名称无法识别的事实,即。 e.我更想看看

login.loginBody.password 而不是 arg0

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

我正在使用 WildFly Swarm 2017.6.0。据我所知,这意味着我有 resteasy + resteasy-validator + hibernate-validator

谢谢。

【问题讨论】:

    标签: jax-rs resteasy bean-validation wildfly-swarm


    【解决方案1】:

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

    有了这些,您需要指示 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-module 并从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

    【讨论】:

    • 这对我有用,非常感谢 :) 供参考:我添加了 org.wildfly.swarm:jaxrs-validator 作为 swarm-fraction 并且它开箱即用,仅在 validation.xml 中声明。跨度>
    【解决方案2】:

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

    @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 中来自定义错误消息的格式

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 正如 Thomas 的回答所示,主要问题是在没有显式编译器标志的情况下,没有关于原始参数名称的运行时信息。
      【解决方案4】:

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

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


      变体 #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. 输入&lt;project_dir&gt;/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)。

      【讨论】:

        猜你喜欢
        • 2012-11-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-20
        • 2019-03-25
        • 1970-01-01
        • 2015-10-17
        • 2018-12-11
        相关资源
        最近更新 更多