2

I am implementing a Controller with a GET endpoint that is ought to receive the following request: /mycontroller?myDate=2019-05-01

My data class looks like this

data class MyData(
   val myDate: LocalDate
)

And my controller:

@Controller
class MyController {

  @Get("{?mydata*}")
  fun getMyEndpoint(mydata: MyData)...

}

Micronaut only binds the request in case I provide a default constructor for MyData, thus make myDate nullable or provide a default value - neither of those is what I wish to do.

Is there a way to get this working without providing the default constructor? Jackson e.g. can also handle it.

Thanks :)

Florian Hansen
  • 746
  • 7
  • 19
  • 1
    "I was hoping that there is a way to bind the object itself directly without having to manually recreate the object:" What do you mean? What specifically isn't working? – James Kleeh May 02 '19 at 16:57
  • 1
    "I am implementing a Controller with a GET endpoint that is ought to receive the following simplified data:" - Why do you want to send JSON to the server with a `GET` request? – Jeff Scott Brown May 02 '19 at 21:10
  • In my real application I have about 8-10 parameters in my GET that I have to manually put together to an object. I was hoping that there is a way like in Spring to bind the object itself to the request, like you can do in spring. It works with dates and nested objects, too: https://blog.codecentric.de/en/2017/08/parsing-of-localdate-query-parameters-in-spring-boot/ and https://stackoverflow.com/questions/16942193/spring-mvc-complex-object-as-get-requestparam. @JeffScottBrown I'll edit my question to make it more clear. – Florian Hansen May 03 '19 at 05:36
  • 1
    Never used Micronaut, but it took me 20 seconds to find this, which seems to describe exactly what you want to do: https://docs.micronaut.io/latest/guide/index.html#binding – JB Nizet May 03 '19 at 05:55
  • Yes, I know this works, but it does not give me the flexibility to e.g. parse dates from the parameter list. Or I do not see the way to do so. – Florian Hansen May 03 '19 at 06:12
  • 1
    The way it is done is described right after in the documentation: https://docs.micronaut.io/latest/guide/index.html#_bindable_types – JB Nizet May 03 '19 at 06:16
  • @FlorianHansen I understand your answer to my question but I don't understand why you want it to be a `GET` request. – Jeff Scott Brown May 03 '19 at 13:51
  • I see now that you changed the question and are no longer sending JSON. This is a very different question now. – Jeff Scott Brown May 03 '19 at 13:52
  • Generally POST is used to create resources on the server side. My endpoint is not doing so. I understand that one could also make a POST and use the body with the data but I'd rather have a clean API design and not let technical difficulties enforce my design. :) So I understand that GET is also mit optimal but IT suits netter to my usecase. If I added the default constructor IT would work out well - I just try to find a was to work around it. – Florian Hansen May 03 '19 at 13:57

1 Answers1

2

Full working example:

import io.micronaut.core.convert.ConversionContext
import io.micronaut.core.convert.TypeConverter
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import java.time.LocalDate
import java.util.*
import javax.inject.Singleton

@Controller("/base")
class MyController {

   @Get("/path")
   fun getMyEndpoint(myData: MyData) {
      println("MyData: $myData")
   }

}

data class MyData(
   val myDate: LocalDate
)

@Singleton
class MyDataConverter : TypeConverter<String, MyData> {
   override fun convert(obj: String, targetType: Class<MyData>, context: ConversionContext?): Optional<MyData> =
      Optional.of(MyData(myDate = LocalDate.parse(obj)))
}

Pro tip: if you are using Jackson and Kotlin don't forget to include the jackson-module-kotlin dependency:

compile "com.fasterxml.jackson.module:jackson-module-kotlin:$YourJacksonVersionHere"
Maurice Müller
  • 1,312
  • 3
  • 16
  • 32