scala spray 概念性內容的總結


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 提供更多的語法糖支持。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM