How to use LocalDateTime RequestParam in Spring? I get "Failed to convert String to LocalDateTime"

I use Spring Boot and jackson-datatype-jsr310 with Maven:

 <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.7.3</version> </dependency> 

When I try to use RequestParam with Java 8 date / time type,

 @GetMapping("/test") public Page<User> get( @RequestParam(value = "start", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime start) { //... } 

and test it with this url:

 /test?start=2016-10-8T00:00 

I get the following error:

 { "timestamp": 1477528408379, "status": 400, "error": "Bad Request", "exception": "org.springframework.web.method.annotation.MethodArgumentTypeMismatchException", "message": "Failed to convert value of type [java.lang.String] to required type [java.time.LocalDateTime]; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.RequestParam @org.springframework.format.annotation.DateTimeFormat java.time.LocalDateTime] for value '2016-10-8T00:00'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2016-10-8T00:00]", "path": "/test" } 
+34
source share
8 answers

TL DR - you can capture it as a string using only @RequestParam , or you can have Spring further parse the string in the java date / time class with @DateTimeFormat for the parameter.

@RequestParam enough to get the date you specified after the = sign, however it enters the method as a String . This is why it throws a cast exception.

There are several ways to achieve this:

  1. analyze the date yourself, taking the value as a string.
 @GetMapping("/test") public Page<User> get(@RequestParam(value="start", required = false) String start){ //Create a DateTimeFormatter with your required format: DateTimeFormatter dateTimeFormat = new DateTimeFormatter(DateTimeFormatter.BASIC_ISO_DATE); //Next parse the date from the @RequestParam, specifying the TO type as a TemporalQuery: LocalDateTime date = dateTimeFormat.parse(start, LocalDateTime::from); //Do the rest of your code... } 
  1. Use the Spring feature to automatically parse and wait for date formats:
 @GetMapping("/test") public void processDateTime(@RequestParam("start") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime date) { // The rest of your code (Spring already parsed the date). } 
+20
source

You did everything right :). Here is an example that shows exactly what you are doing. Just @DateTimeFormat your RequestParam using @DateTimeFormat . No special GenericConversionService or manual conversion required in the controller. This blog post writes about this.

 @RestController @RequestMapping("/api/datetime/") final class DateTimeController { @RequestMapping(value = "datetime", method = RequestMethod.POST) public void processDateTime(@RequestParam("datetime") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime dateAndTime) { //Do stuff } } 

I think you had a problem with the format. Everything works fine on my installation.

+49
source

As I added a comment, you can also use this solution in the signature method: @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime start

+14
source

I found a workaround here .

Spring / Spring Boot supports only the date / date and time format in BODY parameters.

This configuration class adds date / date and time support to QUERY STRING:

 @Configuration public class DateTimeFormatConfiguration extends WebMvcConfigurerAdapter { @Override public void addFormatters(FormatterRegistry registry) { DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar(); registrar.setUseIsoFormat(true); registrar.registerFormatters(registry); } } 

This works even if you bind several request parameters to some class (the @DateTimeFormat annotation is @DateTimeFormat in this case):

 public class ReportRequest { private LocalDate from; private LocalDate to; public LocalDate getFrom() { return from; } public void setFrom(LocalDate from) { this.from = from; } public LocalDate getTo() { return to; } public void setTo(LocalDate to) { this.to = to; } } // ... @GetMapping("/api/report") public void getReport(ReportRequest request) { // ... 
+12
source

I ran into the same problem and found my solution here (without using annotations)

... you should at least correctly register the string in the [LocalDateTime] converter in your context so that Spring can use it to automatically do this for you every time you enter String as input and expect [LocalDateTime]. (A large number of converters have already been implemented using Spring and contain core.convert.support in the package, but none of them are associated with the [LocalDateTime] conversion)

So, in your case, you will do this:

 public class StringToLocalDateTimeConverter implements Converter<String, LocalDateTime> { public LocalDateTime convert(String source) { DateTimeFormatter formatter = DateTimeFormatter.BASIC_ISO_DATE; return LocalDateTime.parse(source, formatter); } } 

and then just register your bean:

 <bean class="com.mycompany.mypackage.StringToLocalDateTimeConverter"/> 

With annotations

add it to your ConversionService:

 @Component public class SomeAmazingConversionService extends GenericConversionService { public SomeAmazingConversionService() { addConverter(new StringToLocalDateTimeConverter()); } } 

and finally you must @Autowire in your ConversionService:

 @Autowired private SomeAmazingConversionService someAmazingConversionService; 

Read more about conversions with Spring (and formatting) on ​​this site . Be warned that he has a ton of advertising, but I definitely found it to be a useful site and a good introduction to this topic.

+3
source

The following works well with Spring Boot 2.1.6:

controller

 @Slf4j @RestController public class RequestController { @GetMapping public String test(RequestParameter param) { log.info("Called services with parameter: " + param); LocalDateTime dateTime = param.getCreated().plus(10, ChronoUnit.YEARS); LocalDate date = param.getCreatedDate().plus(10, ChronoUnit.YEARS); String result = "DATE_TIME: " + dateTime + "<br /> DATE: " + date; return result; } @PostMapping public LocalDate post(@RequestBody PostBody body) { log.info("Posted body: " + body); return body.getDate().plus(10, ChronoUnit.YEARS); } } 

Dto classes:

 @Value public class RequestParameter { @DateTimeFormat(iso = DATE_TIME) LocalDateTime created; @DateTimeFormat(iso = DATE) LocalDate createdDate; } @Data @Builder @NoArgsConstructor @AllArgsConstructor public class PostBody { LocalDate date; } 

Test class:

 @RunWith(SpringRunner.class) @WebMvcTest(RequestController.class) public class RequestControllerTest { @Autowired MockMvc mvc; @Autowired ObjectMapper mapper; @Test public void testWsCall() throws Exception { String pDate = "2019-05-01"; String pDateTime = pDate + "T23:10:01"; String eDateTime = "2029-05-01T23:10:01"; MvcResult result = mvc.perform(MockMvcRequestBuilders.get("") .param("created", pDateTime) .param("createdDate", pDate)) .andExpect(status().isOk()) .andReturn(); String payload = result.getResponse().getContentAsString(); assertThat(payload).contains(eDateTime); } @Test public void testMapper() throws Exception { String pDate = "2019-05-01"; String eDate = "2029-05-01"; String pDateTime = pDate + "T23:10:01"; String eDateTime = eDate + "T23:10:01"; MvcResult result = mvc.perform(MockMvcRequestBuilders.get("") .param("created", pDateTime) .param("createdDate", pDate) ) .andExpect(status().isOk()) .andReturn(); String payload = result.getResponse().getContentAsString(); assertThat(payload).contains(eDate).contains(eDateTime); } @Test public void testPost() throws Exception { LocalDate testDate = LocalDate.of(2015, Month.JANUARY, 1); PostBody body = PostBody.builder().date(testDate).build(); String request = mapper.writeValueAsString(body); MvcResult result = mvc.perform(MockMvcRequestBuilders.post("") .content(request).contentType(APPLICATION_JSON_VALUE) ) .andExpect(status().isOk()) .andReturn(); ObjectReader reader = mapper.reader().forType(LocalDate.class); LocalDate payload = reader.readValue(result.getResponse().getContentAsString()); assertThat(payload).isEqualTo(testDate.plus(10, ChronoUnit.YEARS)); } } 
+1
source

The answers above did not help me, but I came across one that worked here: https://blog.codecentric.de/en/2017/08/parsing-of-localdate-query-parameters-in-spring- boot / Winning fragment There was a ControllerAdvice annotation that has the advantage of applying this hotfix to all of your controllers:

 @ControllerAdvice public class LocalDateTimeControllerAdvice { @InitBinder public void initBinder( WebDataBinder binder ) { binder.registerCustomEditor( LocalDateTime.class, new PropertyEditorSupport() { @Override public void setAsText( String text ) throws IllegalArgumentException { LocalDateTime.parse( text, DateTimeFormatter.ISO_DATE_TIME ); } } ); } } 
+1
source

You can add to the configuration, this solution works with both optional and optional parameters.

 @Bean public Formatter<LocalDate> localDateFormatter() { return new Formatter<>() { @Override public LocalDate parse(String text, Locale locale) { return LocalDate.parse(text, DateTimeFormatter.ISO_DATE); } @Override public String print(LocalDate object, Locale locale) { return DateTimeFormatter.ISO_DATE.format(object); } }; } @Bean public Formatter<LocalDateTime> localDateTimeFormatter() { return new Formatter<>() { @Override public LocalDateTime parse(String text, Locale locale) { return LocalDateTime.parse(text, DateTimeFormatter.ISO_DATE_TIME); } @Override public String print(LocalDateTime object, Locale locale) { return DateTimeFormatter.ISO_DATE_TIME.format(object); } }; } 
0
source

Source: https://habr.com/ru/post/1258860/


All Articles