最近用spray做点东西,刚开始入门,把doc大概过了一遍,最灵活的要数它的routing-DSL了。当然由于scala强大的特性,很多不同于Java的东西,源码看起来还是相当费劲的,尤其是对于我这种scala使用没多久的新手。
今天碰到的问题是如何把参数转化成想要的类型,比如?age=25&birth=1988-01-23,能接收为Int和Date参数。很快在文档里找到了方法,如下写法:
case class Color(red: Int, green: Int, blue: Int) val route = path("color") { parameters('red.as[Int], 'green.as[Int], 'blue.as[Int]) { (red, green, blue) => val color = Color(red, green, blue) doSomethingWith(color) // route working with the Color instance } }
但是我在自己写的时候,发现这样写Date会导致编译错误,
val route: Route = path("stat" / Rest) { path => parameters('age.as[Int], 'birth.as[java.util.Date]) { (age, birth) => get { complete { path + age.toString + birth } } } }
[info] Compiling 1 Scala source to D:\study\spray-template\target\scala-2.11\classes... [error] D:\study\spray-template\src\main\scala\com\example\MyService.scala:60: too many arguments for method parameters: (pdm: spr ay.routing.directives.ParamDefMagnet)pdm.Out [error] parameters('age.as[Int], 'birth.as[java.util.Date]) { (age, birth) => [error] ^
于是首先ctrl+b(idea的快捷键,用了idea后我再也不想换回eclipse了,这不是广告)点进去之后发现是一个case class NameReceptacle,不是我想象的直接返回类型A。继续看parameters,点进去之后是:
def parameters(pdm: ParamDefMagnet): pdm.Out = pdm()
implicit def forNR[T](implicit fsod: FSOD[T]) = extractParameter[NameReceptacle[T], T] { nr ⇒ filter(nr.name, fsod) }可以看到它需要一个implicit的FSOD?是这个东西
import spray.httpx.unmarshalling.{ FromStringOptionDeserializer ⇒ FSOD, _ }
type FromStringOptionDeserializer[T] = Deserializer[Option[String], T]
事情比较清楚了,spray确实没理由强大到随便给个类型A它都能把string转化为A。原来是需要一个Deserializer来做这件事情,因为是implicit,所以你不需要显示写出来。看样子'age.as[Int]应该是spray默认有定义一个Deserializer[Option[String], Int]即把String转化为Int的implicit函数。那Date不行报错,应该就是没有Deserializer[Option[String], Date],那自己定义看看吧。
implicit def string2Date = new Deserializer[Option[String], Date] { override def apply(v1: Option[String]): Deserialized[Date] = v1 match { case None => Left(ContentExpected) case Some(str) => { val sdf = new SimpleDateFormat("yyyy-MM-dd") try { val date = sdf.parse(str) Right(date) } catch { case _: Throwable => Left(MalformedContent("format yyyy-MM-dd")) } } } }
后来发现object Deserializer里有一个方法,可以把普通函数f:A=> B“提升”为Deserializer
implicit def fromFunction2Converter[A, B](implicit f: A ⇒ B) = new Deserializer[A, B] { def apply(a: A) = { try Right(f(a)) catch { case NonFatal(ex) ⇒ Left(MalformedContent(ex.toString, ex)) } } }
implicit def str2Date(str: String) = { val sdf = new SimpleDateFormat("yyyy-MM-dd") sdf.parse(str) }
最后说一个小技巧,一开始没明白'age.as[Int]是什么类型,一种方法是ctrl+b点进去看看,还有一种,你可以定义一个
val x = 'age.as[Int]
val x: NameReceptacle[Int] = 'age.as[Int]
scala确实很强大灵活,学习曲线有高,继续努力。。。。。。。。