Проверка бина с помощью JAX-RS (простой отдых): имя параметра не распознано

Я использую ресурсы 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;
}

Хотя я в целом согласен с этим шаблоном сообщения, что на самом деле раздражает, так это тот факт, что исходное имя параметра не распознается, т.е. е. я бы предпочел увидеть

login.loginBody.password вместо arg0.

Есть ли простой способ исправить это, например. г. как-то указать явное имя для этого параметра?

Я использую WildFly Swarm 2017.6.0. Из того, что я узнал, это означает, что у меня есть resteasy + resteasy-validator + hibernate-validator

Спасибо.


person omilke    schedule 13.07.2017    source источник


Ответы (4)


Вы можете попытаться скомпилировать свое приложение с помощью -parameters или поручить IDE сделать это, например. в случае eclipse: настройки -> java -> компилятор -> «хранить информацию о параметрах метода (можно использовать через отражение)»

После этого вам нужно указать инфраструктуре проверки компонентов (например, ) hibernate-validator использовать ReflectiveParameterNamer через META-INF/validation.xml.

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

См. также Настройка Hibernate Validator.

У меня есть кое-что, надежно работающее с библиотекой 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-namer модуль jboss и сослаться на этот модуль из 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":"[email protected]"}' -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

person Thomas Darimont    schedule 18.07.2017
comment
Это работает для меня, большое спасибо :) для справки: я добавил org.wildfly.swarm:jaxrs-validator как фракцию роя, и это сработало из коробки только с объявлением в validation.xml. - person omilke; 18.07.2017

Существует очень простое решение: вы можете установить собственное сообщение об ошибке в определении ограничения следующим образом.

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

В валидации.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

person Justin Rowe    schedule 20.06.2018

Можете ли вы попытаться реализовать сопоставитель исключений для ConstraintViolationExceptions и посмотреть, поможет ли имеющаяся там информация (список нарушений ограничений) получить имя параметра?

person Santiago Perez Chavez    schedule 16.07.2017
comment
Как показал ответ Томаса, основная проблема заключалась в том, что не было информации о времени выполнения для имени исходных параметров без явного флага компилятора. - person omilke; 18.07.2017

Обновленная версия @thomas-darimont для Hibernate Validator 6.X.

Вариант №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 возвращает имена параметров, полученные из API отражения Java.


Вариант №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.ReflectionParameterNameProvider устарел, используйте вместо него org.hibernate.validator.parameternameprovider.ParanamerParameterNameProvider.

Вопрос: Могу ли я настроить это только в стиле Java-кода? К сожалению, нет. (Подробности см. здесь).

person moleksyuk    schedule 16.11.2018