Spray参数转化

最近用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]                 ^

错误完全不知道在说什么,经典的方法,把错误信息google一下也没有找到满意的答案。然后我开始自己思考,spray或者scala没理由强大到能你随便写一个类型A,as[A]就能把string转化好的啊,一定有什么magic的事情在背后默默地发生了。

于是首先ctrl+b(idea的快捷键,用了idea后我再也不想换回eclipse了,这不是广告)点进去之后发现是一个case class NameReceptacle,不是我想象的直接返回类型A。继续看parameters,点进去之后是:

def parameters(pdm: ParamDefMagnet): pdm.Out = pdm()

然后我就傻了,然后就是一堆implicit的东西,完全不知道在讲什么!然后去
doc里找,看到了
The Magnet Pattern
 然后似乎看到了希望,点开,好长一篇E文啊,心生胆怯!不过后来还是坚持看完了,简单来说就是scala下一种为了解决传统的method overloading由于类型擦除等造成的一些不足的设计模式吧。然后由于‘age.as[Int]返回的是NameReceptacle[Int]类型,就找到了“重载”的方法

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"))
        }
      }
    }
  }

再编译,没问题,运行也ok!

后来发现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]

然后把鼠标移动到x,alt+enter,选择"add type annotation to value definition"就变成

val x: NameReceptacle[Int] = ‘age.as[Int]

这在某些情况下还是很方便的。

scala确实很强大灵活,学习曲线有高,继续努力。。。。。。。。

时间: 2024-10-07 23:39:28

Spray参数转化的相关文章

caffe——网络参数转化

在训练网络时可以利用别人的pre-train model来初始化的网络,caffe可以实现两个网络参数的转化,前提条件是转化的层的参数设计是一致的,以下程序是转化了三个卷积层和三个全连接层的参数,python的代码如下: import caffe caffe.set_mode_gpu() train_net = caffe.Net('/home/python_code/caffe/trainmodel.prototxt', '/home/python_code/caffe/gendernet_5

url地址数据参数转化JSON对象(js三种方法实现)

当我们用get方法提交表单时,在url上会显示出请求的参数组成的字符串,例如:http://localhost:3000/index.html?phone=12345678901&pwd=123123,在服务器端我们要获取其中的参数来进行操作,这种情况下,就要对请求过来的网址进行拆解了.下面将用3种方法实现: 1.js原生方法 思路:先通过split拆解?得到字符串phone=12345678901&pwd=123123   ,然后在通过split拆解&符号左右的字符串,最后再通过

springMVC前台传入字符串日期参数转化

项目中,对象属性日期为Date类型,而前台传入日期为字符串类型,使用到SpringMVC框架,需进行转换. 注意使用springMVC框架,前台传入时间参数,需实现initBinder方法把时间字符串转换为Date对象.实现了该方法,会自动完成封装Date类型属性对象. @InitBinder public void initBinder(WebDataBinder binder) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyy

把url参数转化成一个对象返回

var readUrlToParams = function () { var url = location.href; var nameValue; var paraString = url.substring(url.indexOf("?") + 1, url.length).split("&"); var paraObj = {}; for (var i = 0; nameValue = paraString[i]; i++) { var name =

es6函数的rest参数和拓展运算符(...)的解析

es6的新特性对函数的功能新增加了rest参数和...的拓展运算符.这是两个什么东西呢? 先来看一个问题:如何获取一个函数除了定义的参数之外的其他参数?传统的做法是借助函数的arguments关键字来获取的. 方法如下: function add(a,b){ var i=2,rest=[]; if(arguments.length>2){ for(i;i<arguments.length;i++){ rest.push(arguments[i]); } } return rest; } con

python命令行参数处理

使用sys.argv获得参数,使用getopt和optparse两个内建的模块处理命令行参数: getopt,只能简单处理 命令行参数: optparse,它功能强大,而且易于使用,可以方便地生成标准的.符合Unix/Posix 规范的命令行说明. sys.argv: """ 将参数转化成字典形式 python testargv1.py -o data.txt -i sql.txt --->sql.txt {'-o':'data.txt','-i':'sql.txt'}

LoadRunner变量到参数的互换

作者QQ:764714258,转载请说明出处,阅读此文需要良好的C基础 LoadRunner中,web性能测试使用的脚步是C语言编写的.C语言中有变量的概念,LoadRunner工具中带有参数这个概念,变量和参数在LoadRunner中不是同一个东西 1.参数的声明(暂时先叫声明,就是你可以叫定义一个参数) lr_save_string("hello world","param");  //定义一个参数,并且把”hello world”保存到参数 2.LoadRun

Python函数的可变长参数

# 非关键字可变长参数(元组*args),把N个非关键字参数参数转化为元组.# 当函数被调用的时候,所有形参(必须的和默认的)都将赋值给函数声明中对应的局部变量,剩下的非关键字参数按顺序插入到一个元组中.# 可变长参数元组必须在位置参数和默认参数之后.def func(arg1, arg2 = 9, *args):    print("arg1: %d" % arg1)    print("arg2: %d" % arg2)    for i in args:   

ajax请求中传递的参数中如果含有特殊字符怎么处理?

在JQUERY AJAX开发中遇到了下面的一个问题,在执行AJAX请求的时候,需要传递一个“50%”的参数,而这个参数中含有特殊字符%.这样的话就会出现问题了,我们知道服务器在发送请求的时候,会将url中的参数转化成类似于“%2C%2F%3F%3A%40%26%3D%2B%24%23”,所以在我们执行含有这样特殊字符的ajax请求的时候就会出现错误,因为我们传递的参数可能发生了变化,也可能由于参数本身中就含有特殊字符所以系统没有办法编译它. 可能会抛出这样的一个异常: 警告: Parameter