簡單、有趣的Kotlin異步微服務框架(一)
1. 寫在前面
繼「Flutter異步編程相關的系列文章」已經(jīng)講解完畢后,大概已經(jīng)兩周沒有發(fā)布文章,因為我又開始在籌劃了另一個Kotlin相關的專題: 「Kotlin微服務框架Ktor」。這次專題主要是介紹Kotlin在服務端應用的一個微服務異步web框架。
「為什么又開始Kotlin服務端Ktor專題呢?」
?因為之前在2020年度總結文章也說過,后續(xù)會主要focus在Kotlin、Flutter、面試三大主題展開一系列文章總結,所以開始「Kotlin服務端Ktor專題」也是在規(guī)劃路線之中。不僅會涉及到Kotlin服務端專題后續(xù)還會在Kotlin jetpack Compose方面、Flutter系列都會展開。
?
「原來Flutter專題會繼續(xù)嗎?此次Kotlin服務端Ktor專題文章,F(xiàn)lutter學習者有必要看嗎?」
?「Flutter專題當然會繼續(xù)」,此次開始「Kotlin服務端Ktor專題」是希望最后會通過「Ktor作為API服務后端框架+Flutter作為頁面前臺」搭建一套完整的從前到后的全棧應用。我們都知道作為一名客戶端或前端開發(fā)者,上手后端門檻是比較高的。比如Java語言的Spring、SpringBoot框架,Go、python相關的后端框架等等。而現(xiàn)在的「Ktor」就是一套非常非常簡單、輕量級的異步Kotlin后端框架, 它比SpringBoot更加輕量級,僅僅只需少量代碼就能快速搭建一套API后端服務。
?
個人還是強烈推薦Flutter學習者學習下 , 「特別是有了Kotlin基礎的Android開發(fā)者」 。對于我們大前端開發(fā)者很多的能力思維還僅僅停留在大前端,但是如果你能有一項后端開發(fā)的技能,那么你思考問題和解決問題角度就會不一樣的。當然我們不需要像后端開發(fā)者那樣很精通后端開發(fā)領域,但是如果能掌握后端基本的開發(fā)和使用,還是有很大的幫助的。至少之前作為一名Flutter開發(fā)者,學習完后可以自己從前端UI頁面搭建到后端API設計、數(shù)據(jù)表設計擼一整套的應用。為什么不推薦客戶端開發(fā)者去直接學Spring或SpringBoot,說真的因為框架東西比較多,成本比較高,所以這次Ktor這個微服務框架簡單且易用,學習成本較低,所以還是值得試試的。以下這張圖就是后續(xù)Ktor專題路線的規(guī)劃,下面就直接進入正題~
2. 什么是Ktor
2.1 Ktor基本介紹
用Ktor官方(https://ktor.io/)一句話來介紹: 「Ktor是一個用于創(chuàng)建微服務、web應用程序等異步框架,它很簡單、有趣并且免費開源」。它是由jetbrains官方開源,目前已經(jīng)有8.2K+ star (https://github.com/ktorio/ktor),該框架在國內大家可能比較陌生但是在國外還是很受歡迎的,Ktor可以說是為Kotlin中異步而生的框架,它最底層基于Kotlin Coroutine協(xié)程框架,支持了Client、Server雙端異步特性并且在Client、Server雙端上對WebSocket、Socket有了很好的支持。此外它整體具有以下幾種特性:
「輕量級」
Ktor框架可以說是非常輕量級,僅僅有一些Ktor基礎引擎內容,并沒有冗雜一些其他的功能,甚至日志功能都沒有,但是你可以任意選擇定制你僅僅需要的功能,以構件形式可插拔地集成到Ktor框架中。
「可擴展性強」
可擴展性可以說是Ktor框架又一大亮點之一,Ktor框架的本質就Pipeline管道,任何的功能構件都可以可插拔方式集成在Pipeline中。比如Ktor官方提供一系列構件用于構建所需的功能,使用起來非常簡單方便。
「多平臺」
借助「Kotlin Multiplatform」技術構建,可以在任何地方部署Ktor應用程序.
「異步」
Ktor底層是基于Kotlin協(xié)程構建的,Ktor的異步具有很高的可伸縮性,并且利用其非阻塞式特性,從此擺脫了異步回調地獄。
2.2 Ktor的架構組成
Ktor Framework主要分為以下幾層,最底層核心是Kotlin協(xié)程和基本SDK,然后往上是Ktor核心基礎層,包括了引擎、管道、構件、路由、監(jiān)控等;再往上就是四大主要功能模塊分別是Client模塊、Server模塊、Socket模塊、WebSocket模塊。那么該專題主要是focus在Server模塊,主要利用Server模塊來構件web后端服務。關于WebSocket實際上Ktor分別在Client WebSocket和Server WebSocket兩個層面都給了很大的支持。后續(xù)會基于WebSocket使用構建一個實時IM應用的例子。所以整體上來看Ktor框架還是比較簡單和輕量級的,最為功能豐富在于它的功能構件(Feature), 幾乎后續(xù)所有web后端服務功能都可以看成作為它的一個功能構件(Feature)集成到Ktor中,比如序列化(gson、jackson)、日志、auth認證、template模版(freemarker、velocity)、CORS(解決跨域問題配置)、Session等功能
3. 如何構建一個簡單的Ktor Server應用
構建一個Ktor Server應用可以說是非常非常簡單,僅僅只需簡單十幾行代碼就構建一個Server服務。而構建Ktor Server應用主要分為兩種 「: 一種是通過embeddedServer方式構建,另一種則是通過EngineMain方式構建?!?/strong>
3.1 通過「embeddedServer方式構建」
通過embeddedServer函數(shù)構建Ktor Server應用是一種最為簡單的方式也是官方默認推薦使用的一種方式。embeddedServer函數(shù)是通過在代碼中配置服務器參數(shù)并快速運行應用程序的簡單方法,不需要額外配置文件。比如在下面的代碼段中,它接收服務器容器引擎類型和端口參作為參數(shù),傳入Netty服務器容器引擎和端口8080,啟動應用后就會在8080端口監(jiān)聽。
Application.kt
package com.mikyou.ktor.samplecom.mikyou.ktor.sample
import io.ktor.application.*
import io.ktor.http.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
fun main(args: Array<String>) {
embeddedServer(Netty, port = 8080) {//除了支持Netty還支持Jetty、Tomcat、CIO(Coroutine-based I/O)
routing {
get("/") {
call.respondText("Hello Ktor")
}
}
}.start(wait = true)
}
3.2 通過EngineMain方式構建
EngineMain方式則是選定的引擎啟動服務器,并加載外部一個 application.conf 文件中指定的應用程序模塊. 然后在 application.conf 配置文件中配置應用啟動參數(shù),比如服務監(jiān)聽端口等
Application.kt
package com.mikyou.ktor.sample
import io.ktor.application.*
import io.ktor.response.*
import io.ktor.routing.*
fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)
fun Application.module(testing: Boolean = false) {//該module函數(shù)實際上是Application的擴展函數(shù),要想該函數(shù)運行需要通過application.conf中配置該函數(shù)
routing {
get("/") {
call.respondText("Hello Ktor")
}
}
}
application.conf
ktor {
deployment {
port = 8080 //配置端口
}
application {
modules = [ com.mikyou.ktor.sample.ApplicationKt.module ] //配置加載需要加載的module函數(shù)
}
}
4. 如何架構一個成熟的Ktor應用
由上面可知構建一個簡單的Ktor Server可以說是非常簡單,然而要構建一個成熟的Ktor Server應用也是類似,主要是多了一些如何模塊化組織業(yè)務模塊和更清晰化去架構業(yè)務。主要分為以下7個步驟:
4.1 選擇構建Server的方式
構建Ktor Server應用主要分為兩種: 「一種是通過embeddedServer方式構建,另一種則是通過EngineMain方式構建?!?/strong> 具體的選擇使用方式參考上面第3節(jié)
4.2 選擇Server Engine
要想運行Ktor服務器應用程序,就需要首先創(chuàng)建和配置服務器。服務器配置其中就包括服務器引擎配置,各種引擎特定的參數(shù)比如主機地址和啟動端口等等。Ktor支持大多數(shù)目前主流的Server Engine,其中包括:
Tomcat Netty Jetty CIO(Coroutine-based I/O)
此外Ktor框架還提供一種類型引擎TestEngine專門供測試時使用。要想使用上述指定的Server Engine,就需要添加Server Engine相關的依賴,Ktor是既支持Gradle來管理庫的依賴也支持Maven來管理。

4.3 配置服務參數(shù)
配置服務引擎參數(shù),由于構建Server方式不同,所以配置引擎參數(shù)也不一樣。對于embeddedServer函數(shù)方式構建的Ktor應用可以直接通過代碼函數(shù)參數(shù)方式指定,對于EngineMain方式則通過修改配置文件 application.conf 。
4.3.1 embeddedServer函數(shù)方式
fun main(args: Array<String>) {
embeddedServer(Tomcat, port = 8080) {//配置了服務器引擎類型和啟動端口
routing {
get("/") {
call.respondText("Hello Ktor")
}
}
}.start(wait = true)
}
//除了服務器引擎類型和啟動端口的配置,還支持一些參數(shù)的配置
fun main() {
embeddedServer(Netty, port = 8080, configure = {
connectionGroupSize = 2 //指定用于接收連接的Event Group的大小
workerGroupSize = 5 //指定用于處理連接,解析消息和執(zhí)行引擎的內部工作的Event Group的大小,
callGroupSize = 10 //指定用于運行應用程序代碼的Event Group的大小
}) {
routing {
get("/") {
call.respondText("Hello Ktor")
}
}
}.start(wait = true)
}
//設置可以定制一個EngineEnvironment用于替代默認的ApplicationEngineEnvironment,我們可以通過源碼可知,embeddedServer函數(shù)內部默認構建一個ApplicationEngineEnvironment。
fun main() {
embeddedServer(Netty, environment = applicationEngineEnvironment {
log = LoggerFactory.getLogger("ktor.application")
config = HoconApplicationConfig(ConfigFactory.load())
module {
main()
}
connector {
port = 8080
host = "127.0.0.1"
}
}).start(true)
}
4.3.2 EngineMain方式
如果是選擇EngineMain方式構建Server, 那么就需要通過修改 applicaton.conf
ktor {
application {
modules = [ com.mikyou.ktor.sample.ApplicationKt.module ] //配置加載需要加載的module模塊,這里配置實際上就是Application中module擴展函數(shù)
}
}
//除了可以配置需要加載module模塊,還可以配置端口或主機,SSL等
ktor {
deployment {
port = 8080 //配置端口
sslPort = 8443 //配置SSL端口
watch = [ http2 ]
}
application {
modules = [ com.mikyou.ktor.sample.ApplicationKt.module ] //配置加載需要加載的module模塊
}
security {//配置SSL簽名和密鑰
ssl {
keyStore = build/test.jks
keyAlias = testkey
keyStorePassword = test
privateKeyPassword = test
}
}
}
//application.conf文件包含一個自定義jwt(Json Web Token)組,用于存儲JWT設置。
ktor {
deployment {
port = 8080 //配置端口
sslPort = 8443 //配置SSL端口
watch = [ http2 ]
}
application {
modules = [ com.mikyou.ktor.sample.ApplicationKt.module ] //配置加載需要加載的module模塊
}
security {//配置SSL簽名和密鑰
ssl {
keyStore = build/test.jks
keyAlias = testkey
keyStorePassword = test
privateKeyPassword = test
}
}
jwt {//JWT配置
domain = "https://jwt-provider-domain/"
audience = "jwt-audience"
realm = "ktor sample app"
}
}
預定義屬性

命令行運行
可以使用command運行ktor的jar,并且指定端口
java -jar sample-app.jar -port=8080
可以通過config參數(shù)指定xxx.conf的路徑
java -jar sample-app.jar -config=xxx.conf
還可以通過-P指定運行應用程序代碼的Event Group的大小
java -jar sample-app.jar -P:ktor.deployment.callGroupSize=7
代碼中讀取application.conf中的配置
代碼中讀取application.conf中配置是一件很實用的操作,比如連接數(shù)據(jù)庫時配置都可以通過自定義屬性來實現(xiàn)。比如下面這個例子:
ktor {
deployment {//預定義屬性
port = 8889
host = www.youkmi.cn
}
application {
modules = [ com.mikyou.ApplicationKt.module ]
}
#LOCAL(本地環(huán)境)、PRE(預發(fā)環(huán)境)、ONLINE(線上環(huán)境)
env = LOCAL//自定義屬性
security {//把db相關配置放入security,日志輸出會對該部分內容用*進行隱藏處理
localDb {//自定義屬性localDb
url = "jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"
driver = "com.mysql.cj.jdbc.Driver"
user = "xxx"
password = "xxx"
}
remoteDb {//自定義屬性remoteDb
url = "jdbc:mysql://192.168.0.101:3306/mydb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai"
driver = "com.mysql.cj.jdbc.Driver"
user = "xxx"
password = "xxx"
}
}
}
在appliction.conf自定義了屬性配置后,如何在Ktor代碼獲取呢?請看如下代碼:
const val KEY_ENV = "ktor.env"
//自定義屬性的key,就是根據(jù)配置中層級通過.連接,有點類似JSON的取值調用
const val KEY_LOCAL_DB_URL = "ktor.security.localDb.url"
const val KEY_REMOTE_DB_URL = "ktor.security.remoteDb.url"
const val KEY_LOCAL_DB_DRIVER = "ktor.security.localDb.driver"
const val KEY_REMOTE_DB_DRIVER = "ktor.security.remoteDb.driver"
const val KEY_LOCAL_DB_USER = "ktor.security.localDb.user"
const val KEY_REMOTE_DB_USER = "ktor.security.remoteDb.user"
const val KEY_LOCAL_DB_PWD = "ktor.security.localDb.password"
const val KEY_REMOTE_DB_PWD = "ktor.security.remoteDb.password"
fun Application.configureDb(vararg tables: Table) {
//獲取當前Env環(huán)境
//通過Application中environment實例對象拿到其config對象,通過config以key-value形式獲取配置中的值,不過只支持獲取String和List
val env = environment.config.propertyOrNull(KEY_ENV)?.getString() ?: "LOCAL"
val url = environment
.config
.property(if (env == "LOCAL") KEY_LOCAL_DB_URL else KEY_REMOTE_DB_URL)//如果是LOCAL環(huán)境就切換到本地數(shù)據(jù)庫連接方式
.getString()
val driver = environment
.config
.property(if (env == "LOCAL") KEY_LOCAL_DB_DRIVER else KEY_REMOTE_DB_DRIVER)
.getString()
val user = environment
.config
.property(if (env == "LOCAL") KEY_LOCAL_DB_USER else KEY_REMOTE_DB_USER)
.getString()
val pwd =environment
.config
.property(if (env == "LOCAL") KEY_LOCAL_DB_PWD else KEY_REMOTE_DB_PWD)
.getString()
//連接數(shù)據(jù)庫
Database.connect(url = url, driver = driver, user = user, password = pwd)
//創(chuàng)建數(shù)據(jù)庫表
transaction {
tables.forEach {
SchemaUtils.create(it)
}
}
}
4.4 通過Features添加必要功能構件
在Ktor中一個最典型的請求(Request)-響應(Response)管道模型大致是這樣的: 它從一個請求開始,該請求會被路由到特定的程序處理,并經(jīng)由我們的應用程序邏輯處理,最后做出響應。然而在實際的應用開發(fā)中,并不會這么簡單的,但是本質上Pipeline是不變的。那么在Ktor如何更加將這個簡單管道模型給豐富起來呢?那就是向管道模式添加各種各樣的Feature(功能構件或者功能插件)。
4.4.1 向管道模型添加功能構件
在許多應用開發(fā)中經(jīng)常會用到一些基礎通用的功能,比如內容編碼、序列化、cookie、session等,這些基礎通用的功能在Ktor中統(tǒng)稱為**Features(功能構件)。「所有的Features構件都類似一個插件,插入在Request、application Logic和Response切面之間。
」由上圖可知,當一個請求Request進來后,首先會通過Routing路由機制路由給一個特定的Handler進行處理;然而在把Request交由Handler處理之前可能會經(jīng)過若干個Feature處理;然后Handler處理完這個Request請求,就會將Response響應返回給客戶端,然而在將響應發(fā)送給客戶端之前,它還是可能會經(jīng)過若干個Feature處理,最終Response響應返回到客戶端。可以看出整條從Request到Response鏈路就類似一個工廠流水線,每個Feature各司其職。
4.4.2 Routing本質上也是一個Feature
Feature的靈活性和可插拔性非常強大,它可以出現(xiàn)在Request/Response管道模型中任何一個節(jié)點部分。Routing雖然我們稱為路由,但其本質也是一個Feature
4.4.3 如何安裝Feature
一般都是在應用初始化的時候去安裝Feature即可,安裝Feature非常簡單。僅僅幾行 install 即可搞定,如果是非內置的 Feature 還需要自己引入相關lib依賴. 除了使用現(xiàn)有的Feature, 還可以自定義Feature,關于如何自定義Feature屬于Ktor高階命題,后續(xù)再展開。
import io.ktor.features.*
fun Application.main() {
install(Routing)
install(Gson)
//...
}
//除了在main函數(shù)中安裝,還可以在module入口函數(shù)中安裝
fun Application.module() {
install(Routing)
install(Gson)
//...
}
4.5 通過Routing處理請求
Routing本質上也是一個Feature,所以Routing也需要進行install,然后就可以定義Route Handler處理請求了。
4.5.1 安裝Routing路由
import io.ktor.routing.*
install(Routing) {
// ...
}
//或者直接調用Application的routing擴展函數(shù)
import io.ktor.routing.*
routing {
// ...
}
//因為Application的routing擴展函數(shù)內部做了處理,對于未安裝Routing會自動安裝Routing的容錯,可以稍微瞅下源碼
@ContextDsl
public fun Application.routing(configuration: Routing.() -> Unit): Routing =
featureOrNull(Routing)?.apply(configuration) ?: install(Routing, configuration)
//通過源碼可以發(fā)現(xiàn),如果configuration沒有安裝Routing就會自動安裝Routing,所以大家一般看到的Routing都沒有手動install過程,而是直接類似下面的代碼。
fun main(args: Array<String>) {
embeddedServer(Tomcat, port = 8080) {
routing {//直接調用Application的擴展函數(shù)routing,內部做了對于未安裝Routing會自動安裝Routing的容錯處理
get("/") {
call.respondText("Hello Ktor")
}
}
}.start(wait = true)
}
4.5.2 定義路由處理的Handler
可以看下下面最簡單的一個get服務的定義,下面用get源碼來解讀:
fun main(args: Array<String>) {
embeddedServer(Tomcat, port = 8080) {
routing {
get("/") {//可以看到這個處理get請求的handler,它實際上是一個Route的擴展函數(shù),一起來看看源碼
call.respondText("Hello Ktor")
}
}
}.start(wait = true)
}
//Route.get函數(shù)源碼,其實一個Route對象就是一個對應的Handler,
@ContextDsl
public fun Route.get(path: String, body: PipelineInterceptor<Unit, ApplicationCall>): Route {
return route(path, HttpMethod.Get) { //route函數(shù)本質上是一個Route的擴展函數(shù)
handle(body) //通過調用Route對象來處理的請求
}
}
//route函數(shù)本質上是一個Route的擴展函數(shù)
@ContextDsl
public fun Route.route(path: String, method: HttpMethod, build: Route.() -> Unit): Route {
val selector = HttpMethodRouteSelector(method)
return createRouteFromPath(path).createChild(selector).apply(build)//最終調用apply返回Route對象,build是傳入handle(body)執(zhí)行的lambda,
//也就是創(chuàng)建完child后返回一個Route對象,最終再調用它的handle函數(shù)
}
4.6 應用模塊化
為了使得Ktor應用更具有可維護性、靈活性以及,Ktor提供一種思路就是將應用按照業(yè)務維度進行模塊化設計。注意這里模塊化概念并不是在項目中的一個Module,而這里module本質上是一個 Application 的擴展函數(shù)。并且可以在 application.conf 指定某一個或若干個module進行可插拔式的部署和卸載。
然后一個Module又包括了一條或若干條Request/Response的管道模型。
應用模塊代碼例子如下:
//定義一個accountModule,實際上是一個Application的擴展函數(shù)
fun Application.accountModule() {
routing {
loginRoute()
bindPhoneRoute()
getSmsCodeRoute()
registerRoute()
}
}
//在application.conf配置加載對應的accountModule模塊
ktor {
#LOCAL、PRE、ONLINE
env = LOCAL
deployment {
port = 8889
host = www.youkmi.cn
}
application {
//可以在modules動態(tài)配置所需加載Module,第一個com.mikyou.ApplicationKt.module默認是主Module,用于加載一些基礎通用的Features,實現(xiàn)模塊的可插拔式的安裝和卸載
modules = [ "com.mikyou.ApplicationKt.module","com.mikyou.modules.account.AccountModuleKt.accountModule"]//配置accountModule,注意配置路徑,例如定義Account模塊的類文件是AccountModule.kt, 所以它對應類名稱就是AccountModuleKt,所以accountModule模塊類路徑就是com.mikyou.modules.account.AccountModuleKt.accountModule。
}
//...
}
4.7 應用結構化
Ktor在提供靈活性方面提供多種方式來組織和結構化應用。
4.7.1 以文件來形式組織
將單個文件中相關的路由分組管理,比如應用處理訂單和用戶,就會單獨建立兩個文件: OrderRoutes.kt和CustomerRoutes.kt文件分別管理相關路由請求。
OrderRoutes.kt
fun Route.orderByIdRoute() {
get("/order/{id}") {
}
}
fun Route.createOrderRoute() {
post("/order") {
}
}
CustomerRoutes.kt
fun Route.customerById() {
get("/customer/{id}") {
}
}
fun Route.createCustomer() {
post("/customer") {
}
}
4.7.2 以路由定義形式組織
fun Application.accountModule() {
routing {
loginRoute()
bindPhoneRoute()
registerRoute()
}
}
//登錄
private fun Route.loginRoute() {
post("/api/login") {
//...
}
}
//注冊
private fun Route.registerRoute() {
post("/api/register") {
//...
}
}
//綁定手機號
private fun Route.bindPhoneRoute() {
post("/api/bindPhone") {
//...
}
}
5. 使用IntelliJ IDEA快速構建Ktor Server應用
IntelliJ IDEA提供一個Ktor應用插件可以快速構建Ktor Server應用,其中可以借助Ktor插件可視化地安裝各種Feature功能構件。下面會一步一步引導快速構建一個Ktor Server應用。
5.1 安裝Ktor插件
在IDEA中的plugins模塊中,搜索ktor安裝Ktor插件。
安裝完Ktor插件后,restart IDEA。
5.2 創(chuàng)建Ktor應用工程并安裝Features
打開IDEA,點擊new Project, 選擇左邊欄中的"Ktor"應用,然后輸入Project name,選擇項目路徑、選擇構建系統(tǒng)(Groovy Gradle、Kotlin Gradle或Maven)以及選擇對應的服務器容器的引擎(Netty、Tomcat、Jetty、CIO).
點擊next后,就到需要選擇對應安裝的Feature(功能構件),Ktor插件提供了不同類型的Features, 主要有Security、Routing、HTTP、Monitoring、Templating、Serialization、Sockets、Administration幾大類的Feature, 可以按照自己應用的需求,按需安裝即可。Security類型相關的Features:
Routing類型相關的Features: 添加Routing構件用于路由請求的處理
HTTP類型相關的Features: 添加CORS解決跨域訪問問題
監(jiān)控類型相關的Features: 添加監(jiān)控日志構件CallLogging構件
樣式模板類型相關的Features: 添加HTML DSL和CSS DSL構件
序列化類型相關的Features: 添加Gson構件
Sockets類型相關的Features
Administration類型相關的Features
最終,下面是我們安裝的所有Features,點擊Finish即可創(chuàng)建Ktor Server工程
5.3 Ktor應用工程項目結構
可以看到所有安裝的Features都在plugins包中生成,并在Application類main啟動執(zhí)行的入口函數(shù)進行初始化和配置,并且應用程序默認端口為:8080。
Routing Feature默認生成的代碼:

Template Feature默認生成代碼:

序列化Gson Feature默認生成代碼:

5.4 運行Ktor應用

Ktor應用運行起來后,可以通過localhost訪問上述默認生成的頁面:
Routing Feature默認生成的頁面結果: http://localhost:8080/

Template Feature默認生成的html-dsl頁面結果: http://localhost:8080/html-dsl

Template Feature默認生成的html-dsl頁面結果: http://localhost:8080/html-css-dsl

Gson Feature默認生成的頁面結果: http://localhost:8080/json/gson

5.5 Debug Ktor應用


6. 熊貓先生的小總結
到這里,有關Ktor系列專題的入門第一篇文章就結束了。后面會繼續(xù)深入Ktor相關的內容,包括如何在Ktor操作數(shù)據(jù)庫,我們會用到Kotlin中的ORM框架Exposed以及如何處理請求然后包裝輸出restful api給到客戶端使用。后續(xù)安排是每周一篇文章分享,分別是Kotlin和Flutter相關文章交替進行。
感謝關注,熊喵先生愿和你在技術路上一起成長!
