Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala Akka HTTP casting parameter as java.time.ZonedDateTime

I'm working on a REST service using Akka HTTP (in Scala). I would like a parameter that is passed in to a http get request to be converted to the ZonedDateTime type. The code works fine if I try to use String or Int but fails with a ZonedDateTime type. The code would look like so:

parameters('testparam.as[ZonedDateTime])

Here is the error that I'm seeing:

Error:(23, 35) type mismatch;
 found   : akka.http.scaladsl.common.NameReceptacle[java.time.ZonedDateTime]
 required: akka.http.scaladsl.server.directives.ParameterDirectives.ParamMagnet
          parameters('testparam.as[ZonedDateTime]){

If I add more than one parameter to the list I get a different error:

Error:(23, 21) too many arguments for method parameters: (pdm: akka.http.scaladsl.server.directives.ParameterDirectives.ParamMagnet)pdm.Out
          parameters('testparam.as[ZonedDateTime], 'testp2){

I found this in the documentation when I was researching the problem http://doc.akka.io/japi/akka-stream-and-http-experimental/2.0/akka/http/scaladsl/server/directives/ParameterDirectives.html and I tried the workaround of adding import akka.http.scaladsl.server.directives.ParameterDirectives.ParamMagnet along with using Scala 2.11 but the problem persisted.

Could someone please explain what I'm doing wrong and why the ZonedDateTime type doesn't work? Thanks in advance!

Here is a code snippet that should reproduce the problem I'm seeing

import java.time.ZonedDateTime

import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer

import scala.io.StdIn


object WebServer {
  def main(args: Array[String]) {

    implicit val system = ActorSystem("my-system")
    implicit val materializer = ActorMaterializer()
    // needed for the future flatMap/onComplete in the end
    implicit val executionContext = system.dispatcher

    val route =
      path("hello") {
        get {
          parameters('testparam.as[ZonedDateTime]){
            (testparam) =>
              complete(testparam.toString)
          }
        }
      }

    val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)

    println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
    StdIn.readLine() // let it run until user presses return
    bindingFuture
      .flatMap(_.unbind()) // trigger unbinding from the port
      .onComplete(_ => system.terminate()) // and shutdown when done
  }
}
like image 426
archeezee Avatar asked Dec 10 '22 10:12

archeezee


1 Answers

As ZonedDateTime is not natively unmarshalled by Akka-HTTP, you will need to provide a custom unmarshaller to the parameters directive.

This functionality is briefly described in the docs here.

Your unmarshaller can be created from a function by using Unmarshaller.strict, e.g.

val stringToZonedDateTime = Unmarshaller.strict[String, ZonedDateTime](ZonedDateTime.parse)

This example assumes your param is provided in an ISO format. If it's not, you'll need to amend the unmarshalling function.

You can then use the unmarshaller passing it to the parameters directive:

parameters('testparam.as(stringToZonedDateTime)){ testparam =>
  complete(testparam.toString)
}
like image 143
Stefano Bonetti Avatar answered Feb 23 '23 22:02

Stefano Bonetti