Как проверить ввод строки, передаваемой в тип с плавающей запятой 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»)
Я действительно не понимаю проблемы, I want to make sure that all inputs passing is a float type.
— текущая настройка уже останавливает неверный ввод, вы получаете ответ 400, хотя ошибка исходит от парсера json. Ради чего вы хотите, чтобы произошло 99A.99
?
@Stultuske да, я могу передавать действительные значения с плавающей запятой. Однако я хочу убедиться, что все переданные значения являются значениями с плавающей запятой (а не строками и т. д.).
@Chaosfire. Я хочу остановить это. У меня в контроллере есть @Valid
, где он вернет объект проверки. Я хочу включить строку, которая не является допустимым плавающим сообщением.
public boolean isValid(Float value, ConstraintValidatorContext context) { -> как вы думаете, что здесь произойдет, если вы передадите строку?
@Mr.Kenneth Вы уже препятствуете этому, на самом деле еще раньше. Сначала полезная нагрузка десериализуется в BookDTO
. Затем объект проверяется. 99A.99
нельзя десериализовать в число с плавающей запятой, поэтому парсер json дает сбой и никогда не достигает точки проверки объекта, инициированной @Valid
. То, что вы хотите сделать, невозможно, если вы не десериализуете цену в строку - private String price;
, в документах @DecimalMin
указано, что она поддерживает проверку последовательностей символов. Я думаю, это может сработать, если у вас все в порядке со струнами.
@Chaosfire Я добавил дополнительную информацию о том, каков мой ожидаемый и фактический результат. Когда я пытаюсь добавить все 3 неверных данных одновременно (название, жанр, цена), возвращается полный ответ, показывающий ошибку, а остальные 2 даже не объявляются.
@Стултуске. то же сообщение.
После того, как я проделал это взад и вперед, единственное изменение, которое я сделал, которое работает, - это тип переменной с Float на String.
private Float price;
↓
private String price;
Затем измените все затронутые части кода. Я предполагаю, что Java/Spring Boot выдает сообщение об исключении, что типы Float не могут принять String в качестве входных данных (даже до выполнения пользовательской проверки). Я ожидал, что моя пользовательская проверка будет первой, прежде чем Spring Boot скажет, что это невозможно.
В дальнейшем мне, возможно, придется добавить для этого отдельный класс (класс проверки), где он будет принимать все в строке или объекте, а затем проверять его перед передачей в DTO.
Вы пытались удалить букву «А» из значения, которое пытаетесь передать?