2016-10-27 6 views
4

Я использую Spring загрузки и включены jackson-datatype-jsr310 с Maven:Как использовать LocalDateTime RequestParam весной? Я получаю «Не удалось преобразовать строку в LocalDateTime»

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

Когда я пытаюсь использовать RequestParam с Java 8 типа Дата/Время

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

и протестировать его с этим URL:

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

Я получаю следующее сообщение об ошибке:

{ 
    "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" 
} 

ответ

8

@RequestParam достаточно, чтобы захватить дату, которую вы подаете после знака =, однако он входит в метод как 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... 
} 

примечание стороны, если вы используете Spring загрузки веб-страниц, вы можете перепроверить, если это @GetMapping("/url") или @RequestMapping("/url"), если ваше намерение состоит в том, чтобы выполните этот метод из HTTP-запроса.

+0

Уверен, но есть одна серьезная проблема: зачем использовать пользовательский контроллер, если для большинства этих запросов вы можете использовать Spring JPA Repositories? И это место, когда на самом деле возникает проблема с этой ошибкой:/ – thorinkor

+4

Вы также можете использовать это решение в методе подписи: '@RequestParam @DateTimeFormat (iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime start' – Anna

+0

@ Решение Anna отлично работало для меня – MatanRubin

2

Я столкнулся с той же проблемой и нашел свое решение here (без использования аннотаций)

...you must at least properly register a string to [LocalDateTime] Converter in your context, so that Spring can use it to automatically do this for you every time you give a String as input and expect a [LocalDateTime]. (A big number of converters are already implemented by Spring and contained in the core.convert.support package, but none involves a [LocalDateTime] conversion)

Так что в вашем случае вы могли бы сделать это:

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

, а затем просто зарегистрировать компонент:

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

С аннотациями

добавить его в ConversionService:

@Component 
public class SomeAmazingConversionService extends GenericConversionService { 

    public SomeAmazingConversionService() { 
     addConverter(new StringToLocalDateTimeConverter()); 
    } 

} 

и, наконец, вы бы тогда @Autowire в вашем ConversionService:

@Autowired 
private SomeAmazingConversionService someAmazingConversionService; 

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

7

Вы сделали все правильно :). Here - пример, который точно показывает, что вы делаете. Просто Аннотировать свой запросParam с помощью @DateTimeFormat. В контроллере нет необходимости в специальном GenericConversionService или ручном преобразовании. 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 
    } 
} 

У меня возникла проблема с форматом. В моей настройке все работает хорошо.

2

Как я кладу в комментариях, вы также можете использовать это решение в методе подписи: @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime start

0

Я нашел обходной путь here.

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

Этот класс конфигурации добавлена ​​поддержка даты/даты-времени в QUERY STRING:

@Configuration 
public class DateTimeFormatConfiguration extends WebMvcConfigurerAdapter { 

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

Это работает, даже если вы связать несколько параметров запроса к какому-либо классу (@DateTimeFormat аннотацию беспомощного в данном случае):

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) { 
// ...