我被难住了。我有一个域类Period
,具有两个LocalDate
属性start
和end
,以及相应的控制器方法和创建新周期的模板。
问题:即使请求模型显然正确地反序列化为具有两个正确的LocalDate
属性的Period
实例,I仍然在BindingResult
中得到错误,其中属性显然没有转换为LocalDate
,而是保留为String
s。验证失败:
end: Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDate' for property 'end'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.time.LocalDate] for value '2022-06-02'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2022-06-02]
start: Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDate' for property 'start'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.time.LocalDate] for value '2022-06-01'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2022-06-01]
这是域类:
@Entity
@Table(name = "period")
class Period(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0,
@DateTimeFormat(pattern = "yyyy-MM-dd")
var start: LocalDate?,
@DateTimeFormat(pattern = "yyyy-MM-dd")
var end: LocalDate?,
)
这是用于创建新Period的控制器方法:
@PostMapping("/period/add")
fun updatePeriod(@ModelAttribute("period") @Validated period: Period, result: BindingResult, model: Model) : String {
if(result.hasErrors()){
result
.getFieldErrors()
.stream()
.forEach{f -> println(f.getField() + ": " + f.getDefaultMessage())};
return "add-period"
}
else {
periodRepository.save(period)
return "redirect:/period"
}
}
这是thyymleaf模板:
<!DOCTYPE html>
<html>
<head lang="en">
<title th:text="${title}"></title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
</head>
<body>
<h1 th:text="'New Period'"></h1>
<form action="#" th:action="@{/period/add}" th:object="${period}" method="post">
<table>
<tr>
<td>Start Date</td>
<td><input id="start" type="date" name="start"
min="2000-01-01" max="2023-12-31"
th:value="*{start}"
th:field="*{start}"/></td>
</tr>
<tr>
<td>End Date</td>
<td><input id="end" type="date" name="end"
min="2000-01-01" max="2023-12-31"
th:value="*{end}"
th:field="*{end}"/></td>
</tr>
</table>
<input type="submit" value="Submit" />
</form>
这是调试器在控制器中设置if(result.hasErrors()){
断点时告诉我的内容。显然,period
是具有两个LocalDate
属性的Period
实例,每个属性都有年、月和日(由-->
表示)。在BindingResult
内部也是如此。一切都很好!
然而,有两个错误拒绝用于创建日期的字符串值(由==>
指示)。
this = {WebController@13784} nl.tracking.core.web.WebController@a4388f1
period = {Period@13787} nl.tracking.core.domain.Period@111cd8c3
id = 0
--> start = {LocalDate@17522} "2022-06-01"
--> year = 2022
--> month = 6
--> day = 1
end = {LocalDate@17523} "2022-06-02"
result = {BeanPropertyBindingResult@13788} "org.springframework.validation.BeanPropertyBindingResult: 2 errorsnField error in object 'period' on field 'end': rejected value [2022-06-02]; codes [typeMismatch.period.end,typeMismatch.end,typeMismatch.java.time.LocalDate,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [period.end,end]; arguments []; default message [end]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDate' for property 'end'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.time.LocalDate] for value '2022-06-02'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2022-06-02]]nField error in object 'period' on field 'start': rejected value [2022-06-01]; codes [typeMismatch.period.start,typeMismatch.start,typeMismatch.java.time.LocalDate,typeMismatch]; arguments [org.springfra"
--> target = {Period@13787} nl.tracking.core.domain.Period@111cd8c3
autoGrowNestedPaths = true
autoGrowCollectionLimit = 256
beanWrapper = {BeanWrapperImpl@17526} "org.springframework.beans.BeanWrapperImpl: wrapping object [nl.tracking.core.domain.Period@111cd8c3]"
conversionService = {WebConversionService@17527} "ConversionService converters =nt@org.springframework.format.annotation.DateTimeFormat java.lang.Long -> java.lang.String: org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory@4a422ef1,@org.springframework.format.annotation.NumberFormat java.lang.Long -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@35e37f8fnt@org.springframework.format.annotation.DateTimeFormat java.time.LocalDate -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@1f4c8763,java.time.LocalDate -> java.lang.String : org.springframework.format.datetime.standard.TemporalAccessorPrinter@61841139nt@org.springframework.format.annotation.DateTimeFormat java.time.LocalDateTime -> java.lang.String: org.springframework.format.datetime.standard.Jsr310DateTimeFormatAnnotationFormatterFactory@1f4c8763,java.time.LocalDateTime -> java.lang.String : org.springframework.format.datetime.standard.TemporalAcces"
objectName = "period"
messageCodesResolver = {DefaultMessageCodesResolver@17529}
errors = {ArrayList@17530} size = 2
0 = {FieldError@17550} "Field error in object 'period' on field 'end': rejected value [2022-06-02]; codes [typeMismatch.period.end,typeMismatch.end,typeMismatch.java.time.LocalDate,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [period.end,end]; arguments []; default message [end]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDate' for property 'end'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.time.LocalDate] for value '2022-06-02'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2022-06-02]]"
field = "end"
==> rejectedValue = "2022-06-02"
bindingFailure = true
objectName = "period"
==> source = {TypeMismatchException@17556} "org.springframework.beans.TypeMismatchException: Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDate' for property 'end'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.time.LocalDate] for value '2022-06-02'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2022-06-02]"
codes = {String[4]@17557} ["typeMismatch.pe...", "typeMismatch.en...", "typeMismatch.ja...", "typeMismatch"]
arguments = {Object[1]@17558}
==> defaultMessage = "Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDate' for property 'end'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.time.LocalDate] for value '2022-06-02'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2022-06-02]"
1 = {FieldError@17551} "Field error in object 'period' on field 'start': rejected value [2022-06-01]; codes [typeMismatch.period.start,typeMismatch.start,typeMismatch.java.time.LocalDate,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [period.start,start]; arguments []; default message [start]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDate' for property 'start'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.time.LocalDate] for value '2022-06-01'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2022-06-01]]"
fieldTypes = {HashMap@17531} size = 0
fieldValues = {HashMap@17532} size = 0
suppressedFields = {HashSet@17533} size = 0
nestedPath = ""
nestedPathStack = {ArrayDeque@17535} size = 0
--> model = {BindingAwareModelMap@13789} size = 2
"period" -> {Period@13787} nl.tracking.core.domain.Period@111cd8c3
key = "period"
value = {Period@13787} nl.tracking.core.domain.Period@111cd8c3
id = 0
--> start = {LocalDate@17522} "2022-06-01"
--> year = 2022
--> month = 6
--> day = 1
end = {LocalDate@17523} "2022-06-02"
"org.springframework.validation.BindingResult.period" -> {BeanPropertyBindingResult@13788} "org.springframework.validation.BeanPropertyBindingResult: 2 errorsnField error in object 'period' on field 'end': rejected value [2022-06-02]; codes [typeMismatch.period.end,typeMismatch.end,typeMismatch.java.time.LocalDate,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [period.end,end]; arguments []; default message [end]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.time.LocalDate' for property 'end'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.time.LocalDate] for value '2022-06-02'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2022-06-02]]nField error in object 'period' on field 'start': rejected value [2022-06-01]; codes [typeMismatch.period.start,typeMismatch.start,typeMismatch.java.time.LocalDate,typeMismatch]; arguments [org.springfra"
这对我来说没有意义…我到处搜索S/O,但找不到解决方案。大多数人要么使用不匹配的@DateTimeFormat
模式,要么缺乏thymleaf -extras-java8time。
我使用spring-boot 2.6.7和thymeleaf 3.0.15。我已经在依赖项中添加了thymleaf -extras-java8time。
我唯一要做的就是使用:
@field:DateTimeFormat(pattern = "yyyy-MM-dd")
代替:
@DateTimeFormat(pattern = "yyyy-MM-dd")
在我的域类。