3

I've got an endpoint that is:

api/entities/{year}

and my function looks like:

get(@PathVariable(name = "year") Year year)

But when I try to hit the endpoint I, expectedly, get an error that it can't convert from type String to type Year. I obviously need something like a @DateTypeFormat, but that doesn't really work.

Is there a way to format the incoming value to be a Year, or should I rework my API to not use a Year?

Nickknack
  • 827
  • 4
  • 12
  • 26
  • 2
    My guess would be that the api receive a `String` when you call it and try to map it into a `Year`. `Year` does not have a contructor using a String, so it will likely failed ? – RoadEx Apr 16 '18 at 14:09
  • Maybe this can help you: https://stackoverflow.com/questions/17149425/bind-path-variables-to-a-custom-model-object-in-spring – Oz Molaim Apr 16 '18 at 14:09
  • Check `@RequestBody`, I dont thin `@PathVariable` and `@RequestParam` can be objects – sanbhat Apr 16 '18 at 14:13
  • 1
    You could accept it as a `String` and attempt to convert after the endpoint is hit? – dimwittedanimal Apr 16 '18 at 14:14
  • yeah, @RoadEx, that's what it seems like. Since it's just receiving a String, I was curious if there was some sort of formatter that I could use like DateTypeFormat – Nickknack Apr 16 '18 at 14:14
  • You may use custom types when providing a conversion from `String` - see e.g. https://docs.oracle.com/cd/E19798-01/821-1841/gipyw/index.html for an example – Gyro Gearless Apr 16 '18 at 14:15
  • @dimwittedanimal Yeah, I think that's what i'm going to have to do. I just went with accepting it as a String and then passing in Year.parse(year) to the Service. If you want to add that as an answer i'll accept it. – Nickknack Apr 16 '18 at 14:20
  • 1
    I recomend you take a look to this post: https://stackoverflow.com/questions/17149425/bind-path-variables-to-a-custom-model-object-in-spring – Cristian Caram Peñalver Apr 16 '18 at 14:20
  • 1
    Since you weren't specific, is this a `java.time.Year`? Spring ought to support that OOTB by now, and if it doesn't, you should file a feature request. – chrylis -cautiouslyoptimistic- Apr 16 '18 at 14:28
  • @chrylis yeppers! Just a regular ol' Java time.Year – Nickknack Apr 16 '18 at 14:29
  • File it in the Spring JIRA, then. This should be supported by default converter set. (And as a side note, this should *usually* be a query parameter instead of a path part--`/entities?year=`, but that doesn't change the converter question.) – chrylis -cautiouslyoptimistic- Apr 16 '18 at 14:32

3 Answers3

7

You can do something like:

public class YearHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.getParameterType().equals(Year.class);
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter,
                                  ModelAndViewContainer modelAndViewContainer,
                                  NativeWebRequest nativeWebRequest,
                                  WebDataBinderFactory webDataBinderFactory) throws Exception {
        final String yearFromPath = nativeWebRequest.xxx();

        return Year.from(yearFromPath);
    }

}

Then Register it in your spring-context

Jaiwo99
  • 9,687
  • 3
  • 36
  • 53
  • IMHO, this should be the accepted answer. The other answer is more of a workaround. – msp Apr 16 '18 at 14:45
1

You could accept it as a String and attempt to convert after the endpoint is hit?

dimwittedanimal
  • 656
  • 1
  • 13
  • 29
1

An easy way is to accept an Integer and then convert it if you really need a Year type.

You can register your own HandlerMethodArgumentResolver to do the conversion for you although it looks like too much boiler plate for this scenario it is the more elegant.

Another possibility is to handle it as a date, that conversion comes for free:

@PathVariable(name = "year") @DateTimeFormat(pattern = "yyyy") Date date

You can later extract the year value in the form you prefer.

Paizo
  • 3,986
  • 30
  • 45