最近用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确实很强大灵活,学习曲线有高,继续努力。。。。。。。。
原文:http://my.oschina.net/magicly007/blog/316085