4

The akka-http 2.4.7 reference states that it's possible to do custom Deserializers to convert query parameters, without storing them in an intermediate variable:

"amount".as[Int] extract value of parameter "amount" as Int, you need a matching Deserializer in scope for that to work (see also Unmarshalling)

"amount".as(deserializer) extract value of parameter "amount" with an explicit Deserializer

However, the Deserialized parameter sample on that page does not show how custom deserializers are being used.

How do I define one, so that I can say, e.g. .as[MyType]?

I think the documentation is at fault, because I cannot find a Deserializer mentioned anywhere in akka sources: search

enter image description here

Screenshot on how Deserializer is typeset in the Akka docs.

akauppi
  • 17,018
  • 15
  • 95
  • 120

2 Answers2

5

A Deserializer is just a name for an Unmarshaller in akka-http 2.4.7 (same for akka-http 2.4.8)

Edit: Let's say you're extracting a query parameter named type, and you want a Deserializer that can pattern-match that type parameter from a String to a MyType.

Your route that's in your server app may look like this:

object MyServer {
  ...
  // Assuming that you're requiring a
  // `type` parameter from a `POST` request

  import MyType._

  val route =
    post {
      parameter('type.as(myTypeDeserializer)) { myTypeValue =>
        complete {
          // `myTypeValue` is already pattern-matched
          // to type `MyType` here thanks to `myTypeDeserializer`
          ...
        }
      }
    }
  ...
}

Your MyType object may look like this:

object MyType {
  case object Type1 extends MyType
  case object Type2 extends MyType
  case object Type3 extends MyType

  import akka.http.scaladsl.unmarshalling.Unmarshaller

  // Here we pattern match the query parameter,
  // which has type `String`, to a `MyType`
  val stringToMyType = Unmarshaller.strict[String, MyType] {
    case "type1" => MyType.Type1
    case "type2" => MyType.Type2
    case "type3" => MyType.Type3
  }
}

Hence akka-http will automatically throw a BadRequest response if user requests an unsupported type parameter.

Bamboo Le
  • 51
  • 3
  • I thought they could be the same. But if so, why is `Deserializer` in the code font: http://doc.akka.io/docs/akka/2.4.7/scala/http/routing-dsl/directives/parameter-directives/parameters.html#Description It implies it's an interface or a class of its own. Also, I want to convert query parameter -> case class, nothing to do with HttpEntities that your sample handles. – akauppi Jul 12 '16 at 08:15
  • Glad you showed the `Unmarshaller.strict` parameter is actually a partial function. Can use that and it would make my solution (below) also simpler. – akauppi Jul 13 '16 at 18:57
1

I was able to declare the custom query parameter marshaller right, based on the PredefinedFromStringUnmarshallers.scala built-in samples.

implicit val desId: Unmarshaller[String,Context.Id] = Unmarshaller.strict[String, Context.Id] { s =>
  Context.Id.parseOpt(s).getOrElse(
    if (s=="") throw Unmarshaller.NoContentException
    else throw new IllegalArgumentException( s"Not a valid id: '$s'" )
  )
}

Providing the return type explicitly seems to matter.

akauppi
  • 17,018
  • 15
  • 95
  • 120