如果這是一個XY問題,我很抱歉。如何在編譯時以編程方式創建驗證合同?
TL;博士:
我想有[Request.type, Response.type]
類型的編譯時間圖,所以我可以有效地說,如果我發送消息Request
,一個CLI應,在編譯時,知道如何反序列化其預期的Response
,而不管它直到運行時才知道發送了什麼類型的請求。
太長;仍然閱讀:
我有一個CLI與HTTP服務器通信,並根據發送到HTTP服務器的消息類型,我想驗證JSON響應對案例。
舉例來說,如果我發送HTTP服務器的AddFoo
消息,我可能要驗證JSON響應可以反序列化到AddedFoo
等
我目前的解決方案是相當哈克。使用play-json,我試圖使用從config.mode
(即,發佈到CLI的命令)到預期響應的隱含Reads
的映射來解析JSON響應。
我的代碼看起來是這樣的:
val modeToResponseReads: Map[String, Reads[_]] = Map(
Modes.ADD_FOO -> AddedFoo.addedFooReads,
Modes.ADD_BOO -> AddedBoo.addedBooReads,
Modes.GET_WOO -> GetWooResponse.getWooReads,
)
parser.parse(args, MyConfig()) match {
case Some(config) => try {
val exec = new MyHttpExecutor(remoteUri, config)
val res = Await.result(exec.getResponse, 100.seconds)
// passing `Reads` to `as` because JsValue#as[T] cannot be
// applied at runtime -- only compile-time.
val _ = Json.parse(res.json.toString)
.as(modeToResponseReads(config.mode))
exec.actorSystem.terminate()
exec.wsClient.close()
} catch {
case t: Throwable => logger.error(t.getMessage)
}
case None => {
logger.error("Bad arguments.")
sys.exit(1)
}
}
雖然這個工程,那就是變得越來越難以維護與越來越多的消息令人難以置信的雜牌。此外,我發現這種模式需要在需要進行某種類型的驗證或轉換時進行復制(例如,將Future[Any]
轉換爲Future[AddedFoo]
)。
當然,我的方法是不正確的......這是傳統上的做法嗎?如果方法正確(請不要),是否可以優化?
你的意思是,在*運行時*過去了,發出HTTP請求時,意思? –
我只會嘗試在類型都擴展一些密封特徵或抽象類的時候這樣做,因爲只有類匹配才能幫助您在此之後進行排序。 –
@MichaelZajac所有的請求都會擴展'MyBaseRequest',所有的響應都會擴展'MyBaseResponse'。我發現這是一個相當頑皮的問題......我認爲這將是一個衆所周知的模式。 :) – erip