共用的抽象模型(客戶及伺服器端)- 轉/解碼 以及 Json/XML 處理
轉碼/解碼
HTTP 規範 定義了一個 Content-Encoding 頭域用於標示 HTTP 信息中的正文內容為 「已轉碼」,以及對應的演算法。而在該頭域中唯一通用的轉碼演算法則是壓縮類演算法。
目前 Akka HTTP 支持 HTTP 請求和響應的加/解壓演算法(gzip 或 deflate 轉碼)。相關的核心邏輯在 akka.http.scaladsl.coding 包中
伺服器端
相關支持並沒有自動開啟,而需要特意標明。使用 路由 DSL 啟動信息的 轉/解碼功能請參考 CodingDirectives.
客戶端
目前客戶端並沒有對於響應信息的高階或自動化的解碼功能支持。
以下的例子可以作為手動處理響應信息解碼的參考。
源碼鏈接
import akka.actor.ActorSystemimport akka.http.scaladsl.Httpimport akka.http.scaladsl.coding.{ Gzip, Deflate, NoCoding }import akka.http.scaladsl.model._, headers.HttpEncodingsimport akka.stream.ActorMaterializerimport scala.concurrent.Futureimplicit val system = ActorSystem()implicit val materializer = ActorMaterializer()import system.dispatcherval http = Http()val requests: Seq[HttpRequest] = Seq( "https://httpbin.org/gzip", // Content-Encoding: 響應信息為gzip "https://httpbin.org/deflate", // Content-Encoding: 響應信息為deflate "https://httpbin.org/get" // 響應信息無 Content-Encoding).map(uri ? HttpRequest(uri = uri))def decodeResponse(response: HttpResponse): HttpResponse = { val decoder = response.encoding match { case HttpEncodings.gzip ? Gzip case HttpEncodings.deflate ? Deflate case HttpEncodings.identity ? NoCoding } decoder.decode(response)}val futureResponses: Future[Seq[HttpResponse]] = Future.traverse(requests)(http.singleRequest(_).map(decodeResponse))
JSON /XML 相關支持
Akka HTTP 的編集/反編集架構使得開發者能相對容易地為其應用開發的數據結構提供對應的通訊格式(如JSON,XML甚至二進位格式)。
Akka HTTP 目前通過 spray-json (結合 akka-http-spray-json 模塊)提供相應的 JSON 支持。 而 XML 則是 通過 Scala XML (結合 akka-http-xml 模塊) 提供相應 XML 支持。
如果官方模塊不符合開發者需求,社區也提供了其它的 JSON 庫以供選擇。可參考相關列表。
Spray-json 支持
SprayJsonSupport trait 為各種(已經擁有 spray.json.RootJsonReader 和/或 spray.json.RootJsonWriter 的)類型 T 提供了一個 FromEntityUnmarshaller[T] 和 ToEntityMarshaller[T] 。
(譯註:??的,這個官方解釋等於沒解釋...隱函知識點太多。簡單幾句,即使在 Akka HTTP 體系以外,Scala 的 JSON 與 T 類型間轉換很多時候需要依賴對應的 Reader/Writer 進行和 格式轉換。這個可以另外找時間看看。而放到 Akka HTTP 這個架構下,則需要再為 mashaller/unmarshaller 提供二次轉換的機制。如果原有的 Reader/Writer 存在的話,那麼這個二次轉換的封裝 可以自動完成,參考下文細節。)
如需啟用 spray-json 的編集/反編集功能的自動化支持,請在應用中加入以下的依賴庫
"com.typesafe.akka" %% "akka-http-spray-json" % "10.0.3"
然後,為開發者所需的類型提供一個 RootJsonFormat[T] 並引入到當前可視域中. 具體做法可以參考 spray-json 的相關文檔。
(譯註:Format 本身就是 Reader/Writer 的合併類型,這是算是 Scala 比較主流的一種設計模式)
最後,如以下的例子般從 SprayJsonSupport 中直接導入隱式參數 FromEntityUnmarshaller[T] 以及 ToEntityMarshaller[T] 或者 糅合 akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport trait 至開發者相對應的 JSON 支持模塊中
當上述步驟完成後, 開發者應該可以無縫使用客制類型 T 與 JSON 間的 編集和反編集 轉換。
import akka.http.scaladsl.server.Directivesimport akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupportimport spray.json._// 領域模型final case class Item(name: String, id: Long)final case class Order(items: List[Item])// 把開發者的 json 格式處理(Format 本身提供 Reader/Writer)全放到一個輔助 trait 里:trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol { implicit val itemFormat = jsonFormat2(Item) implicit val orderFormat = jsonFormat1(Order) // contains List[Item]}// 當需要 json 編集/反編集 時使用class MyJsonService extends Directives with JsonSupport { // format: OFF val route = get { pathSingleSlash { complete(Item("thing", 42)) // 會編集為 JSON } } ~ post { entity(as[Order]) { order => // 會從 JSON 反編集到 Order val itemsCount = order.items.size val itemNames = order.items.map(_.name).mkString(", ") complete(s"Ordered $itemsCount items: $itemNames") } } // format: ON}
美化輸出
spray-json 會預設把開發者的類型用 CompactPrinter 這個隱式轉換工具以緊湊型的格式輸出 JSON, 如下:
implicit def sprayJsonMarshallerConverter[T](writer: RootJsonWriter[T])(implicit printer: JsonPrinter = CompactPrinter): ToEntityMarshaller[T] = sprayJsonMarshaller[T](writer, printer)
如果開發者需要類型以美化型的格式輸出 JSON,可以引入 PrettyPrinter 到當前可視域進行隱式轉換。
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._import spray.json._// 領域模型final case class PrettyPrintedItem(name: String, id: Long)object PrettyJsonFormatSupport { import DefaultJsonProtocol._ implicit val printer = PrettyPrinter implicit val prettyPrintedItemFormat = jsonFormat2(PrettyPrintedItem)}// 當需要 json 編集/反編集時使用class MyJsonService extends Directives { import PrettyJsonFormatSupport._ // format: OFF val route = get { pathSingleSlash { complete { PrettyPrintedItem("akka", 42) // will render as JSON } } } // format: ON}val service = new MyJsonService// 確認 JSON 以美化格式輸出Get("/") ~> service.route ~> check { responseAs[String] shouldEqual """{""" + "
" + """ "name": "akka",""" + "
" + """ "id": 42""" + "
" + """}"""}
相關例子詳情 參考鏈接
更多關於 Spray-Json 開發的 文檔.
Scala XML 支持
ScalaXmlSupport trait 提供了一個 FromEntityUnmarshaller[NodeSeq] 和 ToEntityMarshaller[NodeSeq] 以便開發者直接使用或者二次開發相關的 XML 編集/反編集功能。
開發者可以按照以下的方式啟用 Scala XMLNodeSeq 提供的 XML 編集/反編集支持
- 添加依賴庫 "com.typesafe.akka" %% "akka-http-xml" % "1.x".
- 導入 akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._ 或者糅入 akka.http.scaladsl.marshallers.xml.ScalaXmlSupport trait.
當上述步驟完成後, 開發者應該可以無縫使用客制類型 T 與 XML 及 NodeSeq 間的 編集和反編集 轉換。
相關例子詳情 參考鏈接
推薦閱讀:
※你覺得python的字典和json差不多嗎?
※豆瓣閱讀器通過 JSON 獲取文章內容,看到的數據格式是被混淆/加密的,這是通過什麼原理實現的?如何將其解碼?
※如何從CFG轉換到State Machine?
※從零開始的 JSON 庫教程(五):解析數組
※XML/HTML/JSON——數據抓取過程中不得不知的幾個概念
