Beliebte Suchanfragen

Cloud Native

DevOps

IT-Security

Agile Methoden

Java

//

Parsing of LocalDate query parameters in Spring Boot

6.8.2017 | 4 minutes of reading time

Spring Boot is a framework that follows the convention over configuration principle. Whenever a convention does not do the right thing for you, it is usually easily possible to configure the desired behavior. As I have discovered lately, there is currently no easy way to configure the parsing behavior for LocalDate instances used as query parameters. In this blog post I will present my findings as well as the idiomatic solution. The complete code shown in this blog post is available at GitHub .

LocalDate as query parameter

Spring MVC provides a simple API for defining web endpoints. Say I want to have an end point which returns all orders for a date given as a query parameter, so I can query it like this:


GET /orders?date=2017-08-04

I could implement this using Spring Boot and Spring MVC like this:

1@RestController
2@RequestMapping("/orders")
3public class OrderController {
4 
5  @GetMapping
6  public ResponseEntity<List<Order>> getOrdersByDate(
7       @RequestParam(name = "date")
8       @DateTimeFormat(iso = ISO.DATE)
9       LocalDate date) {
10    // retrieve and return orders by date
11  }
12}

Note that I have to specify a @DateTimePattern and set it to ISO.DATE. This enables parsing of query parameters with pattern yyyy-MM-dd. Now lets say I want the query parameter to be optional (and return all the orders, if no date query parameter is given). This can be configured by setting the required option of the @RequestParam annotation to false. Spring will pass null to my handler method if no query parameter is given. Since I don’t like to work with null in my code, I’d rather use an Optional as parameter type:

1@GetMapping
2public ResponseEntity<List<Order>> getOrdersByDate(
3     @RequestParam(name = "date", required = false)
4     @DateTimeFormat(iso = ISO.DATE)
5     Optional<LocalDate> date) {
6  // retrieve and return orders by date
7}

Configuring the pattern globally – first approach

The examples above work well for relatively small applications. But for bigger applications I want to configure the pattern for my date query parameters in one central place. Unfortunately there is currently no configuration property in Spring Boot that can be used for this. The spring.mvc.date-pattern property only applies to parameters of type java.util.Date. So after googling a while I found this StackOverflow answer , which boils down to implementing a @ControllerAdvice:

1@ControllerAdvice
2public class LocalDateControllerAdvice {
3 
4  @InitBinder
5  public void initBinder(WebDataBinder binder) {
6    binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
7      @Override
8      public void setAsText(String text) throws IllegalArgumentException {
9        LocalDate.parse(text, DateTimeFormatter.ISO_DATE);
10      }
11    });
12  }
13}

The controller advice gets applied to all controller instances. It uses the WebDataBinder to register a custom editor for the LocalDate class. Inside the editor implementation the predefined DateTimeFormatter.ISO_DATE is used to parse strings into LocalDate. While this is not as convenient as a configuration property, it looks good enough for me. And if we try it out, if works for GET requests without date query parameter. But what happens if I try to query the orders for a specific date again? Let’s see:


GET /orders?date=2017-07-12

HTTP/1.1 400
Connection: close
Content-Type: application/json;charset=UTF-8
Date: Fri, 04 Aug 2017 13:32:54 GMT
Transfer-Encoding: chunked

{
    "error": "Bad Request",
    "exception": "org.springframework.web.method.annotation.MethodArgumentTypeMismatchException",
    "message": "Failed to convert value of type 'java.lang.String' to required type 'java.util.Optional'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.RequestParam java.time.LocalDate] for value '2017-07-12'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [2017-07-12]",
    "path": "/orders",
    "status": 400,
    "timestamp": "2017-08-04T13:32:54Z"
}

Unfortunately this solution does not work with the Optional parameter.

Idiomatic solution possible changes in future Spring Boot releases

After this setback I decided to have a look at how the spring.mvc.date-pattern property is implemented for java.util.Date. The implementation can be found in WebMvcAutoConfiguration :

1@Bean
2@ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")
3public Formatter<Date> dateFormatter() {
4  return new DateFormatter(this.mvcProperties.getDateFormat());
5}

So it looks like in Spring Boot, instead of registering a custom editor for LocalDate I have to register an implementation of Formatter. So let’s give that a try:

1@Bean
2public Formatter<LocalDate> localDateFormatter() {
3  return new Formatter<LocalDate>() {
4    @Override
5    public LocalDate parse(String text, Locale locale) throws ParseException {
6      return LocalDate.parse(text, DateTimeFormatter.ISO_DATE);
7    }
8 
9    @Override
10    public String print(LocalDate object, Locale locale) {
11      return DateTimeFormatter.ISO_DATE.format(object);
12    }
13  };
14}

This solution does work with optional as well as with non optional parameters. Since it looks like this is missing in Spring Boot, I’ve already created a pull request to add this to Spring Boot . So maybe this will become available as a configuration property in future release.

Conclusion

It is currently not possible to configure the behavior parsing of LocalDate used as query parameters via configuration properties in Spring Boot. One possible solution is to register a custom editor using a @ControllerAdvice. While this solution works for parameters of type LocalDate, it does not work with parameters of type Optional. The idiomatic solution that works with both types, is to implement a Formatter. This solution may become part of Spring Boot itself in future releases.

share post

Likes

0

//

More articles in this subject area

Discover exciting further topics and let the codecentric world inspire you.

//

Gemeinsam bessere Projekte umsetzen.

Wir helfen deinem Unternehmen.

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.