Java с RestEasy: @Valid, но не только на конечной точке

Я оказываюсь в следующем сценарии в RestEasy REST API:

@POST
@Path("my-endpoint")
@Produces(MediaType.APPLICATION_JSON)
public Response myEndpoint(
    final @Nullable Payload payload
) {
    final var result = runCommand(
      new Command(payload),
      withPermissions(CAN_WRITE)
    );

    return ok(result);
}

...

@Value
public class Payload {
    @NotNull Long mSomeValidLong;
}

...

@Value
public class Command {
    @NotNull @Valid mPayload;
}

Как видите, конечная точка не проверяет полезную нагрузку сразу. Вместо этого существует вложенная логика: полезные данные передаются в Command, который затем выполняется методом runCommand. Несмотря на то, что вы не видите, как реализован runCommand, вы можете видеть, что сначала он проверяет разрешения. Он также имеет причудливую логику для рабочих потоков и перехвата исключений.

Итог: именно команда должна проверять параметры (потому что именно тогда мы достигли места, где разрешения контролируются, и когда мы уверены, что у нас есть для этого ресурсы).

Не спрашивайте меня, почему разрешения не проверяются как @Annotation непосредственно над методом конечной точки, это устаревший код.

При такой настройке я наблюдаю следующее:

Это работает (потому что RestEasy идентифицирует метод как один из своих):

public Response myEndpoint(final @Valid @NotNull Payload payload) { ... }

Это не работает, потому что это старая старая Java:

public Command(final @Valid @NotNull Payload payload) { ... } // Constructor as it would be generated by Lombok

Мой вопрос: есть ли вообще способ заставить @Valid творить чудеса где-то еще, кроме параметров метода конечной точки?

Судя по тому, что я прочитал, jakarta.validation будет выполнять точно такую ​​же проверку в частях кода, отличных от RestEasy.

Последующий вопрос: разумно ли иметь две разные конкурирующие системы проверки? (Проверка RestEasy, а также проверка другого типа, например проверка Джакарты)

Когда вы создаете команду с помощью new, Spring не имеет возможности внедрить валидаторы.

cyberbrain 11.06.2024 18:24

Другими словами: Command не является компонентом, управляемым Spring. Проверка с помощью @NotNull или чего-то еще работает только с bean-компонентами, управляемыми весной. Я предполагаю, что ваше определение конечной точки находится внутри объекта, помеченного @Controllerили чем-то подобным. В этом случае проверка работает и на уровне параметров метода.

Thilo Schwarz 11.06.2024 20:02

Это не конкурирующие системы проверки, они абсолютно одинаковы. Вы также используете не Spring MVC, а Джерси (независимо от того, оба поддерживают @Valid). Однако в Spring MVC это часть выполнения метода, а не при вызове обычных методов. Вы можете использовать @Validated в своем классе для запуска проверки вызовов методов, но здесь это тоже не сработает, поскольку runCommand является вызовом внутреннего метода (и это не работает с вызовами внутренних методов). Таким образом, в качестве обходного пути вы можете вручную выполнить проверку или использовать полноценный AspectJ для поддержки вызовов внутренних методов.

M. Deinum 11.06.2024 20:12

@ThiloSchwarz Есть ли более простая аннотация, чем @ Controller, чтобы заставить платформу внедрить свои механизмы проверки в класс? т. е. я не хочу превращать команду или объект, выполнивший команду, в контроллер.

jeancallisti 12.06.2024 09:12

@M.Deinum Я думаю, что на самом деле мы используем JAX-RS, а не Spring или Jersey. Виноват.

jeancallisti 12.06.2024 09:13

@M.Deinum Я подтверждаю, что мы используем RestEasy/JAX-RS. Итак, вы утверждаете, что единственный способ здесь — запустить проверку команды вручную?

jeancallisti 12.06.2024 17:31

Добавление @Valid в конструктор ничего не даст ни для Spring, ни для Jersey. С его помощью вы можете проверять аргументы метода для конечных точек JAX-RS или контроллеров Spring. Но для конструкторов это не подойдет. Если вы поместите его в другой метод, вызываемый из метода обработки, если вы не добавите какой-нибудь AOP (как упоминалось ранее), он тоже не будет работать.

M. Deinum 13.06.2024 15:42

На этот вопрос ответил stackoverflow.com/questions/78613823/…? Какую версию RESTEasy вы используете?

James R. Perkins 14.06.2024 18:30

@JamesR.Perkins, нет, это совсем другой вопрос. этот вопрос касается ручного применения проверки в других областях кода, чем те, где она изначально была автоматизирована.

jeancallisti 14.06.2024 18:42
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
9
83
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

замените @Valid собственным @Validated Spring

Попробую, но думаю, дело не в этом. Казаться. Комментарий Дейнума под моим вопросом.

jeancallisti 12.06.2024 09:09
Ответ принят как подходящий

Это может быть решением: Как вручную запустить проверку Spring?

1. Внедрить валидатор

@Autowired
private Validator validator;

2. Используйте его вручную

validator.validate(myObject);

Валидатор может быть валидатором по умолчанию:

// JSR-303 Validator
import javax.validation.Validator;

...или какой-нибудь причудливый валидатор, например, валидатор, предоставленный Spring:

// Spring Validator
import org.springframework.validation.Validator;

Другие вопросы по теме