Как проверить ввод строки, передаваемой в тип с плавающей запятой dto

Как проверить ввод строки, передаваемой в тип с плавающей запятой dto?

Я могу передавать любые значения с плавающей запятой от 1,0 до 99,99. Однако, когда я прохожу через 99A.99, он возвращает ошибку 400 Bad Request.

JSON parse error: Cannot deserialize value of type `java.lang.Float` from String \"99A.99\": not a valid `Float` value

DTO

public class BookDTO {

    @NotNull
    @DecimalMin(value = "0.00", inclusive = true, message = "Price should not be less than 0.00")
    @DecimalMax(value = "99.99", inclusive = true, message = "Price should not be more than 99.99")
    private Float price;

    public Float getPrice() {
        return price;
    }

    public void setPrice(Float price) {
        this.price = price;
    }
}

Я хочу убедиться, что все передаваемые входные данные имеют тип с плавающей запятой. На самом деле не нашел удачи при проверке Интернета. Я проверил

Ниже то, что я пробовал:

ВалидФлоатФормат

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = FloatFormatValidator.class)
public @interface ValidFloatFormat {

    String message() default "Invalid float format";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

FloatFormatValidator

public class FloatFormatValidator implements ConstraintValidator<ValidFloatFormat, Float> {

    @Override
    public boolean isValid(Float value, ConstraintValidatorContext context) {
        if (value == null) {
            return true; // Let @NotNull handle null values
        }

        try {
            Float.parseFloat(String.valueOf(value)); // Try parsing to check format
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }
}

ДТО (обновлено)

    @NotNull
    @ValidFloatFormat(message = "INVALID FLOAT")
    @DecimalMin(value = "0.00", inclusive = true, message = "Price should not be less than 0.00")
    @DecimalMax(value = "99.99", inclusive = true, message = "Price should not be more than 99.99")
    private Float price;

Я все еще получаю ту же ошибку неправильного запроса.

Дополнительная информация:

Полный ответ:

{
    "timestamp": "2024-07-11T14:18:03.787+00:00",
    "status": 400,
    "error": "Bad Request",
    "message": "JSON parse error: Cannot deserialize value of type `java.lang.Float` from String \"9a9.99\": not a valid `Float` value",
    "path": "/api/books"
}

Ожидаемый образец ответа (упрощенный):

{
    "timestamp": "2024-07-11T14:29:40.473398987",
    "status": 400,
    "error": "BAD_REQUEST",
    "errors": {
        "genre": [
            "size must be between 5 and 10"
        ],
        "title": [
            "size must be between 5 and 20",
            "Title should be alphanumeric Proper Noun case"
        ]
    }
}

Причина, по которой ожидаемый ответ упрощен вместо стандартного (очень длинного) ответа @Valid:

Контроллер

    public ResponseEntity<?> createBook(@Valid @RequestBody BookDTO bookDTO, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            ValidationErrorResponse validationErrorResponse = new ValidationErrorResponse();
            validationErrorResponse.setStatus(HttpStatus.BAD_REQUEST.value());
            validationErrorResponse.setError(HttpStatus.BAD_REQUEST.name());
            for (FieldError fieldError : bindingResult.getFieldErrors()) {
                validationErrorResponse.addError(fieldError.getField(), fieldError.getDefaultMessage());
            }
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(validationErrorResponse);
        }

        BookDTO createdBook = bookService.createBook(bookDTO);
        return ResponseEntity.status(HttpStatus.CREATED).body(createdBook);
    }

ВалидацияErrorResponse

public class ValidationErrorResponse {

    private Map<String, List<String>> errors = new HashMap<>();

    public Map<String, List<String>> getErrors() {
        return errors;
    }

    ... // other columns

    public void addError(String field, String errorMessage) {
        errors.computeIfAbsent(field, k -> new ArrayList<>()).add(errorMessage);
    }
}

Для недопустимого числа с плавающей запятой я хотел бы вернуть сообщение по умолчанию, которое было установлено в DTO («INVALID FLOAT»)

Вы пытались удалить букву «А» из значения, которое пытаетесь передать?

Stultuske 11.07.2024 14:44

Я действительно не понимаю проблемы, I want to make sure that all inputs passing is a float type. — текущая настройка уже останавливает неверный ввод, вы получаете ответ 400, хотя ошибка исходит от парсера json. Ради чего вы хотите, чтобы произошло 99A.99?

Chaosfire 11.07.2024 14:44

@Stultuske да, я могу передавать действительные значения с плавающей запятой. Однако я хочу убедиться, что все переданные значения являются значениями с плавающей запятой (а не строками и т. д.).

Mr. Kenneth 11.07.2024 14:57

@Chaosfire. Я хочу остановить это. У меня в контроллере есть @Valid, где он вернет объект проверки. Я хочу включить строку, которая не является допустимым плавающим сообщением.

Mr. Kenneth 11.07.2024 14:58

public boolean isValid(Float value, ConstraintValidatorContext context) { -> как вы думаете, что здесь произойдет, если вы передадите строку?

Stultuske 11.07.2024 15:02

@Mr.Kenneth Вы уже препятствуете этому, на самом деле еще раньше. Сначала полезная нагрузка десериализуется в BookDTO. Затем объект проверяется. 99A.99 нельзя десериализовать в число с плавающей запятой, поэтому парсер json дает сбой и никогда не достигает точки проверки объекта, инициированной @Valid. То, что вы хотите сделать, невозможно, если вы не десериализуете цену в строку - private String price;, в документах @DecimalMin указано, что она поддерживает проверку последовательностей символов. Я думаю, это может сработать, если у вас все в порядке со струнами.

Chaosfire 11.07.2024 15:16

@Chaosfire Я добавил дополнительную информацию о том, каков мой ожидаемый и фактический результат. Когда я пытаюсь добавить все 3 неверных данных одновременно (название, жанр, цена), возвращается полный ответ, показывающий ошибку, а остальные 2 даже не объявляются.

Mr. Kenneth 11.07.2024 16:35

@Стултуске. то же сообщение.

Mr. Kenneth 11.07.2024 16:35
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
8
56
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

После того, как я проделал это взад и вперед, единственное изменение, которое я сделал, которое работает, - это тип переменной с Float на String.

private Float price;

private String price;

Затем измените все затронутые части кода. Я предполагаю, что Java/Spring Boot выдает сообщение об исключении, что типы Float не могут принять String в качестве входных данных (даже до выполнения пользовательской проверки). Я ожидал, что моя пользовательская проверка будет первой, прежде чем Spring Boot скажет, что это невозможно.

В дальнейшем мне, возможно, придется добавить для этого отдельный класс (класс проверки), где он будет принимать все в строке или объекте, а затем проверять его перед передачей в DTO.

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