從窗戶飄入陣陣烤肉香,我趕緊揪住老姐,要不然老姐就跑出去了。
「今天放假一天也沒關係的啦。」老姐很不開心。
「再等我一下,快改好了。」我匆匆加上幾行程式碼,然後 run application 測試。
看到結果我放心的鬆開手中的布料。「好了,成功了!」
老姐停下腳步,驚訝的問:「什麼什麼?多房間聊天室架好了?」
「哪有那麼快啦。我是弄好之前說的 API 版本 authenticate 。」我邊說邊悄悄把右手的手機藏到背後打字,傳給烤肉店延後十分鐘到的訊息。「你看,原本的長這樣,需要手動複製很多程式碼。」
get("/api/topics") {
val session = call.sessions.get<LoginSession>()//需要複製的程式碼
session?.let {//需要複製的程式碼
val topics = transaction {
Topic.all().with(Topic::author, Topic::tags).map {
...
}
}
call.respond(topics)
} ?: call.respond(HttpStatusCode.Unauthorized)//需要複製的程式碼
}
「現在只要把目標 API 移進 authenticationAPI<LoginSession> 區塊
。」
authenticationAPI<LoginSession> {
get("/api/topics") {
val topics = transaction {
Topic.all().with(Topic::author, Topic::tags).map {
...
}
}
call.respond(topics)
}
}
「太神了!你怎麼做到的?」老姐瞪大眼睛。她本以為我只會獨立出一個 method 取代 session?.let 的部分。
「我研究了一下 authentication
對 Route 做的事。」要做就做最好的。
inline fun <reified T> Route.authenticationAPI(callback: Route.() -> Unit): Route {
// With createChild, we create a child node for this received Route
val routeAuthenticationAPI = this.createChild(object : RouteSelector(1.0) {
override fun evaluate(context: RoutingResolveContext, segmentIndex: Int): RouteSelectorEvaluation =
RouteSelectorEvaluation.Constant
})
// Intercepts calls from this route at the features step
routeAuthenticationAPI.intercept(ApplicationCallPipeline.Features) {
call.sessions.get<T>() ?: run {
call.respond(HttpStatusCode.Unauthorized)
finish()
return@intercept
}
}
// Configure this route with the block provided by the user
callback(routeAuthenticationAPI)
return routeAuthenticationAPI
}
我語帶驕傲的解釋:「關鍵是 createChild
,沒用它的話就會影響到全部的 route , finish()
表示提前結束,不用再叫原本的 route 做事。然後 session 我用的是 泛型 Generic Type
,因為說不定後面會是 multiple sessions 架構,如果真的是那樣我還可以把 HttpStatusCode 那邊改成參數傳入。」我的手指在鍵盤上蠢蠢欲動。
沒有 finish() 的白做工圖片記錄,一樣去資料庫拿資料,只是沒印在 response 。
「等一下!還有更重要的事!」老姐一臉驚恐。「店裡預約要遲到了!」
好吧,不確定會重複利用的細部就先不模組化設計了。吃肉去!
本次鐵人賽的作品在放進更多內容後已經成書,書名是《老姐要用Kotlin寫專案:從 Server 到 Android APP 的開發生存日記》,歡迎購買唷。https://www.tenlong.com.tw/products/9789864348978