spray 是基于 akka 的轻量级 scala 库,可用于编写 REST API 服务。了解 spray 的 DSL 后可以在很短的时间内写出一个 REST API 服务,它的部署并不需要 tomcat , apche 等容器,可以直接 run。对于每一个 route, spray 都会 sprawn 一个或多个 actor提供服务,actor 的数目是可以配置的,我们不需要关心多线程处理的问题。从 benchmark 来看, spray 的性能是很不错。另外,spray 提供了一套测试套件,testkit,使用它可以在本地测试 API 的可用性,测试功能非常强大,我想它可以完全取代 SoapUI 之类的自动化测试工具

但是,学习使用spray的过程还是比较痛苦的,举个例子

Await.result(statement, 5.seconds)

写下这行代码后,IDE 会抱怨 5.seconds 理解不了,但是它在哪,IDE并不会提示。还有很多情况是,IDE 不会抱怨,但是编译过程会出错,说找不到某个 implicit 变量。接触spray三个月,最让我头疼的就是implicit变量的问题。关于常用的变量的头文件位置,我想写一篇日志记录一下

下面是一些概念性的理解,主要是参考 spray doc,附带部分自己的理解。

Route

type Route = RequestContext => Unit

RequestContext 在 route 里是局部变量,它总是存在的,我们可以在 route 的任何位置调用 RequestContext 的方法。RequestContext 包含     request 和 response 两层含义,既能获得request传过来的数据,也能完成.complete, .reject 等response语义。Route的定义并没有返回值,它的返回值为Unit,这是因为Route 采用的是 fire and forget (tell) 模式,它的好处首先是灵活,没有限制返回值的类型,其次是这种设计是完全的非阻塞的,易于和actor结合

Directive (指令)

spray的 DSL 就是一个个 Directive 拼在一起的

def routes = {
path("") {
get {
respondWithMediaType(`text/html`) {
complete {
DefaultValues.defaultAPI
}
}
}
}
}

path, get, respondWithMediaType, complete 都算是一条 Directive。他们嵌套在一起形成一个 Route 的语义。一般来讲,Directive有四个功能,首先是复制 RequestContext 到下一层 Directive,RequestContext 在传输的过程中是 immutable 的。其次,它可以获取 requester 附带的参数,比如 parameters, formData, jsonData 等,还能完成 marshall, unmarshall 操作。定义路径和某些逻辑来过滤一条请求,比如不符合任意一个 path 的请求将会被 reject,逻辑可以是 header 必须拒绝缓存,post data 只允许 json 格式等等。最后,他可以用返回结果到 requester,可以定义结果的类型和值,在上面的例子上就是 text/html 和 string(defaultAPI)

Reject, Exception, Timeout handling

requester的请求可能格式有错误,可能权限不足,也可能数据包丢失,因此 spray 需要 ”异常“处理。异常处理可以是显式的自定义声明,spray 也有默认的实现。对于那些不符合任何 path(或者符合 reject path), 传输数据出错,没有认证的请求,spray 会调用 reject handling 的实现,程序的执行过程中出现的问题会调用 exception handling定义的操作,比如对于除0异常的处理。 Timeout 最简单,它定义请求在多少时间没有应答就会返回超时错误。

spray 通过 complete(code, message) 实现,浏览器会显示 code, message。对那些比较普遍的错误,spray有自定义的code, message,而那些自定义的错误,我们要手写 code message。

Json support

上面提到directive的四种功能,其中就包括从requestContext中获取parameter和formData。除此之外,spray 还提供从 json 到 case class 的映射,这相当于一个轻量级的 ORM,让我们的逻辑代码写的更加美观。

object Json4sProtocol extends Json4sSupport {
implicit def json4sFormats: Formats = DefaultFormats
}
import models.Json4sProtocol._

json -> case class 有多个库可以选择,我更喜欢 json4s,相比于 spray.json,json4s的mapping更加简单,不需要为每一个case class写一个转换器。此外,json4s来支持json -> case class 的不完全转换,也就是说 json 可以有某些域不存在,但是 case class 对应的那些域必须声明为 Option[]

从 parameter 到 case class 也有映射,

case class Color(keyword: String, sort_order: Int, sort_key: String)

  val testRoute =
path("test") {
parameters('keyword.as[String], 'sort_order.as[Int], 'sort_key.as[String]).as(Color) { color =>
//handleTestRoute(color) // route working with the Color instance
complete {
<h1>test route</h1>
}
}
}

spray test-kit

test-kit 能够实现本地测试 Route,配合 spec2 可以取代 soapUI等自动化测试工具。

"main entrance (/) working" in {
Get("/") ~> routes ~> check {
status === OK
response.entity.asString === DefaultValues.defaultAPI }
}
step {
server.start()
server.createAndWaitForIndex("persons")
} "Elasticsearch person dao" should {
"return None for a non-existing person" in new Context {
val person: Option[Person] = dao.get(UUID.randomUUID)
person must beNone
} "create and then return a person" in new Context {
val id: UUID = dao.add(name = "John")
val person = dao.get(id)
person must beSome[Person]
person must haveName("John")
}
} step {
server.stop()
}

test-kit 支持更丰富的语法糖,比如 beSome, beSome(data)等等,相比于 scalatest,spec2 提供更多的语法糖支持。

scala spray 概念性内容的总结的更多相关文章

  1. Jquery 概念性内容编辑器

      概念性jQuery内容编辑器,这是一款非常有特色的jQuery编辑器,该编辑器支持文字.列表.视频.引用等功能,是一款小巧简洁,富有个性化的jQuery内容编辑器插件. 代码: <!doct ...

  2. 怎样学习Scala泛函编程

    确切来说应该是我打算怎么去学习Scala泛函编程.在网上找不到系统化完整的Scala泛函编程学习资料,只好把能找到的一些书籍.博客.演讲稿.论坛问答.技术说明等组织一下,希望能达到学习目的.关于Sca ...

  3. Scala HandBook

    目录[-] 1.   Scala有多cool 1.1.     速度! 1.2.     易用的数据结构 1.3.     OOP+FP 1.4.     动态+静态 1.5.     DSL 1.6 ...

  4. 使用SBT构建Scala应用(转自git)

    # 使用SBT构建Scala应用 ## SBT简介 SBT是Simple Build Tool的简称,如果读者使用过Maven,那么可以简单将SBT看做是Scala世界的Maven,虽然二者各有优劣, ...

  5. 【原】SBT构建Scala应用

    [转帖] 原文地址:https://github.com/CSUG/real_world_scala/blob/master/02_sbt.markdown 尊重版权,尊重他人劳动成果,转帖请注明原文 ...

  6. Java 8 vs. Scala(一): Lambda表达式

    [编者按]虽然 Java 深得大量开发者喜爱,但是对比其他现代编程语言,其语法确实略显冗长.但是通过 Java8,直接利用 lambda 表达式就能编写出既可读又简洁的代码.作者 Hussachai ...

  7. scala 小结(一)

    Scala 是什么?(What is scala?)   引用百度百科对于scala的定义: Scala是一门多范式的编程语言,一种类似java的编程语言,设计初衷是实现可伸缩的语言.并集成面向对象编 ...

  8. Programming In Scala笔记-第九章、控制抽象

    本章主要讲解在Scala中如何使用函数值来自定义新的控制结构,并且介绍Curring和By-name参数的概念. 一.减少重复代码 1.重复代码的场景描述 前面定义的函数,将实现某功能的代码封装到一起 ...

  9. 使用SBT构建Scala应用【转载】

    使用SBT构建Scala应用 SBT简介 SBT是Simple Build Tool的简称,如果读者使用过Maven,那么可以简单将SBT看做是Scala世界的Maven,虽然二者各有优劣,但完成的工 ...

随机推荐

  1. hbase源码系列(十四)Compact和Split

    先上一张图讲一下Compaction和Split的关系,这样会比较直观一些. Compaction把多个MemStore flush出来的StoreFile合并成一个文件,而Split则是把过大的文件 ...

  2. HTML5里的input标签的required属性的提示

    <input type="text" name="usr_name" required='required' oninvalid="setCus ...

  3. EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.

    启动两个client,过了一会,停了其中一个,访问注册中心时,界面上显示了红色粗体警告信息: 查阅了很多资料,终于了解了中间的问题.现将理解整理如下: Eureka server和client之间每隔 ...

  4. JVM调优总结(一):基本概念

    一.数据类型 Java虚拟机中,数据类型可以分为两类:基本类型和引用类型. 基本类型的变量保存原始值,即:他代表的值就是数值本身: 而引用类型的变量保存引用值.“引用值”代表了某个对象的引用,而不是对 ...

  5. [impala] impala 简介

    [简介] Impala是Cloudera公司主导开发的新型查询系统,它提供SQL语义,能查询存储在Hadoop的HDFS和HBase中的PB级大数据.已有的Hive系统虽然也提供了SQL语义,但由于H ...

  6. Linux epoll版定时器

    #ifndef __MYTIMER_H_ #define __MYTIMER_H_ /*************** 高并发场景下的定时器 *****************/ //定时器回调函数 t ...

  7. 关于Unity中新版动画系统的使用

    Mecanim动画 1:旧版动画系统只能通过代码来控制动画播放,随着动画种类变多,代码复杂度也会增加,同时动画过渡也需要非常繁琐的代码控制,为了让有经验的动画师开发动画,unity推出了针对人物角色的 ...

  8. ibatis中 $ 于 # 的 区别?

    转自: http://www.blogjava.net/lsbwahaha/archive/2009/04/16/266026.html 一个项目中在写ibatis中的sql语句时,where use ...

  9. Yii2 session的使用方法(2)

    yii2打开session use yii\web\Session; $session = Yii::$app->session; // check if a session is alread ...

  10. centos:解决docker容器内挂载目录无权限 ls: cannot open directory .: Permission denied

    docker运行一个容器后,将主机中当前目录下的文件夹挂载到容器的文件夹后 进入到docker容器内对应的挂载目录中,运行命令ls后提示: ls: cannot open directory .: P ...