Keyword: Ktor, Suspend Function
到Day11使用Ktor進行網路請求並且顯示在Android畫面的Code放在
KMMDay11
Ktor是十分強大的網路框架,可以幫助開發者迅速進行網路開發,我們借助其中一小部分功能,來進行網路Api的請求.
今天的範例Api ,我們使用 Cafe Nomad:咖啡廳遊牧民的Api,我常在咖啡廳工作,偶爾也會查一下這個網站.非常感謝.
https://cafenomad.tw/api/v1.2/cafes/{cityName}
網路請求的回傳形式如下,還蠻詳細的.
[{
"id": "000703fe-cf8a-43c8-bd83-c90cfd61915f",
"name": "蜂巢咖啡",
"city": "taipei",
"wifi": 0,
"seat": 4.5,
"quiet": 3,
"tasty": 5,
"cheap": 4,
"music": 4,
"url": "https://www.facebook.com/honeycombcafe2016/",
"address": "新北市永和區永貞路214號",
"latitude": "25.00409680",
"longitude": "121.51528650",
"limited_time": "no",
"socket": "no",
"standing_desk": "yes",
"mrt": "永安市場站",
"open_time": "雙週四公休,有變更另外公告"
}
]
詳細文件可以到以下
https://cafenomad.tw/developers/docs/v1.2
由於我們這次的Api資料是要雙平台一起共用的,也沒有根據平台不同而要特別處理的邏輯,也因此程式碼實作區塊放在shared模組中的commonMain中即可.
為了未來更好閱讀和修改,我推薦在shared底下建立以下結構
model/remote/ktor/
以後會將Ktor所用到的物件放在這裡
首先先根據需要的回傳值建立一個對應的物件,這次的範例不會把整個物件接起來,挑幾個值比較重要的就好了,需要其他的值可以自己加入.
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable//要注意不要import錯了
@Serializable
data class CafeResponseItem(
val id: String, //參數的名稱即是對應的json key
val address: String,
val name: String,
@SerialName("city") val cityName:String //如果json key跟參數不同,可以使用SerialName
)
然後建立一個interface,加入api path
interface CafeApi {
suspend fun fetchCafeFromApi(city:String): List<CafeResponseItem>
}
注意這邊是suspend function,要跑在coroutine環境中.
然後再建立一個此interface的實作CafeApiImpl物件
先建立一個Ktor的HttpClient物件,HttpClient會負責管理連線,我們會根據需求調整HttpClient設定.
private val client = HttpClient {}
首先是JsonFeature的設定,這邊可以設定要用什麼解析器來進行Response的解析,例如常用的Gson等等.
這邊使用我們昨天引入的Kotlinx-Serializer解析器
install(JsonFeature){
serializer = KotlinxSerializer(kotlinx.serialization.json.Json { ignoreUnknownKeys = true })
//由於我們沒有把每個key值接起來,所以還要額外設定忽略未知的key,ignoreUnknownKeys
//注意前面的Json要使用Kotlinx-Serializer內的,有需多同名的Json,要小心不要用錯了.
}
然後是Logging的設定,目前就先簡單的印出來看看數據正確,我們之後會根據各平台來實作不同的Log
LogLevel也可以根據自己需要的程度調整
install(Logging){
logger = object : Logger{
override fun log(message: String) {
println(" CafeApiImpl log: $message" )
}
}
level = LogLevel.INFO
}
最後是網路請求timeout設定
install(HttpTimeout){
val timeout = 20000L
connectTimeoutMillis = timeout
requestTimeoutMillis = timeout
socketTimeoutMillis = timeout
}
最後整個HttpClient設定就會像這樣
private val client = HttpClient {
install(JsonFeature){
serializer = KotlinxSerializer(kotlinx.serialization.json.Json { ignoreUnknownKeys = true })
}
install(Logging){
logger = object : Logger{
override fun log(message: String) {
println(" CafeApiImpl log: $message" )
}
}
level = LogLevel.INFO
}
install(HttpTimeout){
val timeout = 50000L
connectTimeoutMillis = timeout
requestTimeoutMillis = timeout
socketTimeoutMillis = timeout
}
}
有了HttpClient後,就可以利用HttpClient發起網路請求,在使用HttpClient時,需要傳入一個HttpRequestBuilder
override suspend fun fetchCafeFromApi(city :String): List<CafeResponseItem>{
return client.get<List<CafeResponseItem>>{
getCafeListHttpBuilder(city)
}
}
cafeListHttpBuilder的內容如下,在HttpRequestBuilder中不只url,還能設定headers,body等等
private fun HttpRequestBuilder.getCafeListHttpBuilder(path: String) {
url{
if(path.isNotEmpty()){
takeFrom("https://cafenomad.tw/api/v1.2/cafes/$path")
}else{
takeFrom("https://cafenomad.tw/api/v1.2/cafes/")
}
}
}
這樣我們就完成了網路請求流程,明天會在Android上來顯示的網路請求的結果