如何使用 Ktor 快速开发 Web 项目

简介: 如何使用 Ktor 快速开发 Web 项目

一. Ktor 介绍



Ktor 是一个高性能的、基于 Kotlin 的 Web 开发框架,支持 Kotlin Coroutines、DSL 等特性。


Ktor 是一个由 Kotlin 团队打造的 Web 框架,可用于创建异步、高性能和轻量级的 Web 服务器,并使用 Kotlin 惯用的 API 构建非阻塞的多平台 Web 客户端。


Ktor 的服务端仅限于 JVM,但是 Ktor 的客户端是一个 Multiplatform 的库。


如果使用 Kotlin Multiplatform 构建跨平台项目时,使用 Ktor 的客户端作为 Http 框架是一个不错的选择。


Ktor 由两部分组成:服务器引擎和灵活的异步 HTTP 客户端。当前版本主要集中在 HTTP 客户端上。客户端是一个支持 JVM,JS,Android 和 iOS 的多平台库,现在经常在跨平台移动应用程序中使用。


二. Ktor 服务端的使用



我们可以通过多种方式运行 Ktor 服务端程序:

image.png

Ktor Server.png


  • 在 main() 中调用 embeddedServer 来启动 Ktor 应用
  • 运行一个 EngineMain 的 main() 并使用 HOCON application.conf 配置文件
  • 作为 Web 服务器中的 Servlet
  • 在测试中使用 withTestApplication 来启动 Ktor 应用


2.1 Gradle 配置 Ktor


Kotlin 的版本需要 1.3.x,因为 Ktor 底层会依赖到 Kotlin Coroutines。


在需要使用 Ktor 的 module 中添加如下的依赖:

dependencies {
    ...
    implementation "io.ktor:ktor-server-core:${libs.ktor}"
    implementation "io.ktor:ktor-server-netty:${libs.ktor}"
}


后面的例子还会介绍 Ktor 其他的 artifact,例如:freemarker、gson 等。


2.2 embeddedServer


当使用 embeddedServer 时,Ktor 使用 DSL 来配置应用程序和服务器引擎。目前,Ktor 支持 Netty、Jetty、Tomcat、CIO(Coroutine I/O) 作为服务器引擎。(当然,也支持创建自己的引擎并为其提供自定义配置。)


以 Netty 作为服务器引擎为例,通过 embeddedServer 启动 Ktor 应用:

fun main() {
    embeddedServer(Netty, port?:8080, watchPaths = listOf("MainKt"), module = Application::module).start()
}


2.3 ApplicationCall && Routing


当一个请求进入 Ktor 应用时(可以是 HTTP,HTTP / 2 或 WebSocket 请求),该请求将被转换为 ApplicationCall 并通过该应用程序拥有的管道。Ktor 的管道是由一个或多个预先安装的拦截器组成,这些拦截器提供某些功能,例如:路由,压缩等,最终将处理请求。


ApplicationCall 提供对两个主要属性 ApplicationRequest 和 ApplicationResponse 的访问。它们对应于传入请求和传出响应。 除了这些之外,ApplicationCall 还提供了一个 ApplicationEnvironment 和一些有用的功能来帮助响应客户端请求。


Routing 是一项安装在应用程序中的功能,用于简化和构建页面请求处理。Ktor 的 Routing 支持 Restful 的各种方法,以及使用 DSL 进行配置。


Routing 支持嵌套,被称为 Routing Tree,可以通过递归匹配复杂的规则和处理请求。


2.4 CORS


默认情况下,Ktor 提供拦截器以实现对跨域资源共享(CORS)的适当支持。


首先,将 CORS 功能安装到应用中。

fun Application.main() {
  ...
  install(CORS)
  ...
}


Ktor CORS 功能的默认配置仅处理 GET,POST 和 HEAD HTTP 方法以及以下标头:

HttpHeaders.Accept
  HttpHeaders.AcceptLanguages
  HttpHeaders.ContentLanguage
  HttpHeaders.ContentType


下面的例子展示了如何配置 CORS 功能

fun Application.main() {
  ...
  install(CORS)
  {
    method(HttpMethod.Options)
    header(HttpHeaders.XForwardedProto)
    anyHost()
    host("my-host")
    // host("my-host:80")
    // host("my-host", subDomains = listOf("www"))
    // host("my-host", schemes = listOf("http", "https"))
    allowCredentials = true
    allowNonSimpleContentTypes = true
    maxAge = Duration.ofDays(1)
  }
  ...
}


2.5 Packing


部署 Ktor 应用时,可以使用 fat jar 或者 war 包。


我们以 fat jar 为例,使用 gradle 的 shadow 插件可以方便地打包 Ktor 的应用。


在项目根目录下的 build.gradle 中添加 shadow 插件的依赖:

buildscript {
    repositories {
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0'
        ......
    }
}


然后在需要打包的 module 中添加 shadow 插件和输出 jar 包名称以及 jar 包的入口 Main 函数:

plugins {
    id 'java'
    id 'kotlin'
    id 'com.github.johnrengelman.shadow'
}
......
shadowJar {
    baseName = 'xxx'  // jar 包名称
    manifest {
        attributes["Main-Class"] = "xxx.xxx.xxx.xxx"  // jar 包的主函数
    }
}


三. 例子



RxCache 为例,本文会介绍使用 Ktor 开发一个 Local Cache 的 browser(浏览器),用于读取磁盘缓存中的数据。


RxCache 是一款支持 Java 和 Android 的 Local Cache 。目前支持内存、堆外内存、磁盘缓存。


开发的背景:我们存在一些桌面程序部署在 Ubuntu 上,并需要对这些程序进行埋点,而 RxCache 本身支持磁盘的缓存。因此,我使用 RxCache 存储埋点的数据,所以需要一个浏览器的程序来查看本地的埋点数据。


3.1 RxCache 的配置


RxCache 是一个单例,使用时需要先调用 config() 配置 RxCache。


RxCache 支持二级缓存:Memory、Persistence,并拥有多种序列化方式。这些可以通过配置来体现。

val rxCache: RxCache by lazy {
    val converter: Converter = when (Config.converter) {
        "gson"      -> GsonConverter()。
        "fastjson"  -> FastJSONConverter()
        "moshi"     -> MoshiConverter()
        "kryo"      -> KryoConverter()
        "hessian"   -> HessianConverter()
        "fst"       -> FSTConverter()
        "protobuf"  -> ProtobufConverter()
        else        -> GsonConverter()
    }
    RxCache.config {
        RxCache.Builder().persistence {
            when (Config.type) {
                "disk"   -> {
                    val cacheDirectory = File(Config.path) // rxCache 持久层存放地址
                    if (!cacheDirectory.exists()) {
                        cacheDirectory.mkdir()
                    }
                    DiskImpl(cacheDirectory, converter)
                }
                "okio"   -> {
                    val cacheDirectory = File(Config.path) // rxCache 持久层存放地址
                    if (!cacheDirectory.exists()) {
                        cacheDirectory.mkdir()
                    }
                    OkioImpl(cacheDirectory, converter)
                }
                "mapdb"  -> {
                    val cacheDirectory = File(Config.path) // rxCache 持久层存放地址
                    MapDBImpl(cacheDirectory, converter)
                }
                "diskmap"-> {
                    val cacheDirectory = File(Config.path) // rxCache 持久层存放地址
                    DiskMapImpl(cacheDirectory, converter)
                }
                else     -> {
                    val cacheDirectory = File(Config.path) // rxCache 持久层存放地址
                    if (!cacheDirectory.exists()) {
                        cacheDirectory.mkdir()
                    }
                    DiskImpl(cacheDirectory, converter)
                }
            }
        }
    }
    RxCache.getRxCache()
}


3.2 module


Ktor module 是一个开发者定义的函数,它用于接收 Application 类(该类负责配置服务器管道,安装功能,注册路由,处理请求等)。


在本例子中,安装了 DefaultHeaders、CallLogging、FreeMarker、ContentNegotiation、Routing。

fun Application.module() {
    install(DefaultHeaders)
    install(CallLogging)
    install(FreeMarker) {
        templateLoader = ClassTemplateLoader(this::class.java.classLoader, "templates")
        defaultEncoding = "utf-8"
    }
    install(ContentNegotiation) {
        gson {
            setDateFormat(DateFormat.LONG)
            setPrettyPrinting()
        }
    }
    install(Routing) {
         ......
    }
}


3.3 Routing


Routing 提供了对外的页面。

install(Routing) {
        static("/") {
            defaultResource("index.html", "web")
        }
        post("/saveConfig") {
            val postParameters: Parameters = call.receiveParameters()
            Config.path = postParameters["path"] ?: ""
            Config.type = postParameters["type"] ?: ""
            Config.converter = postParameters["converter"] ?: ""
            call.respond(FreeMarkerContent("save.ftl", mapOf("config" to Config)))
        }
        get("/list") {
            val file = File(Config.path)
            val array = file.list()
            call.respond(array)
        }
        get("/detail/{key}") {
            val key = call.parameters["key"]
            val json = rxCache.getStringData(key)
            call.respondText(json)
        }
        get("/info") {
            val json = rxCache.info
            call.respondText(json)
        }
    }


其中 index.html 用于配置 RxCache。


saveConfig 用于展示保存的 RxCache 的数据,其中用到了 FreeMarker 的模板 save.ftl

<html>
<h2>Hi</h2>
RxCache's path: ${config.path} </br>
RxCache's persistence: ${config.type} </br>
RxCache's serialization: ${config.converter} </br>
</html>


list 接口、detail 接口分别用于展示磁盘存储数据的 key,以及根据 key 来查询详细的存储内容。


image.png

list 接口


image.png

detail 接口


info 接口用于显示缓存中的信息。


image.png

info 接口


3.4 启动


browser 配置了 kotlinx-cli,它可以通过命令行解析参数。目前,只支持 '-p' 用于表示启动 Ktor 应用的端口号。


browser 使用 Netty 作为服务器引擎。

fun main(args: Array<String>) {
    val parser = ArgParser("rxcache-browser")
    val port            by parser.option(ArgType.Int, shortName = "p", description = "Port number of the local web service")
    parser.parse(args)
    embeddedServer(Netty, port?:8080, watchPaths = listOf("MainKt"), module = Application::module).start()
}


四. 小结



Ktor 构建的应用,只需少量代码和配置即可完成,非常简便。


非常适用于简单的 Web 项目、对外提供接口的 OpenAPI 项目。当然使用它来构建微服务也是可以,它也有丰富的 Features

相关文章
|
1月前
|
设计模式 前端开发 数据库
Python Web开发:Django框架下的全栈开发实战
【10月更文挑战第27天】本文介绍了Django框架在Python Web开发中的应用,涵盖了Django与Flask等框架的比较、项目结构、模型、视图、模板和URL配置等内容,并展示了实际代码示例,帮助读者快速掌握Django全栈开发的核心技术。
175 45
|
18天前
|
前端开发 安全 JavaScript
2025年,Web3开发学习路线全指南
本文提供了一条针对Dapp应用开发的学习路线,涵盖了Web3领域的重要技术栈,如区块链基础、以太坊技术、Solidity编程、智能合约开发及安全、web3.js和ethers.js库的使用、Truffle框架等。文章首先分析了国内区块链企业的技术需求,随后详细介绍了每个技术点的学习资源和方法,旨在帮助初学者系统地掌握Dapp开发所需的知识和技能。
2025年,Web3开发学习路线全指南
|
25天前
|
缓存 JSON 监控
如何在项目中保证 Web 组件化的性能
保证 Web 组件化的性能需要从多个方面入手,综合运用各种优化方法和策略。通过持续的优化和改进,能够提高组件化的整体性能,为用户提供更好的体验,同时也有助于提高项目的开发效率和质量。
35 8
|
25天前
|
存储 前端开发 JavaScript
如何在项目中高效地进行 Web 组件化开发
高效地进行 Web 组件化开发需要从多个方面入手,通过明确目标、合理规划、规范开发、加强测试等一系列措施,实现组件的高效管理和利用,从而提高项目的整体开发效率和质量,为用户提供更好的体验。
27 7
|
29天前
|
开发框架 搜索推荐 数据可视化
Django框架适合开发哪种类型的Web应用程序?
Django 框架凭借其强大的功能、稳定性和可扩展性,几乎可以适应各种类型的 Web 应用程序开发需求。无论是简单的网站还是复杂的企业级系统,Django 都能提供可靠的支持,帮助开发者快速构建高质量的应用。同时,其活跃的社区和丰富的资源也为开发者在项目实施过程中提供了有力的保障。
|
28天前
|
开发框架 JavaScript 前端开发
TypeScript 是一种静态类型的编程语言,它扩展了 JavaScript,为 Web 开发带来了强大的类型系统、组件化开发支持、与主流框架的无缝集成、大型项目管理能力和提升开发体验等多方面优势
TypeScript 是一种静态类型的编程语言,它扩展了 JavaScript,为 Web 开发带来了强大的类型系统、组件化开发支持、与主流框架的无缝集成、大型项目管理能力和提升开发体验等多方面优势。通过明确的类型定义,TypeScript 能够在编码阶段发现潜在错误,提高代码质量;支持组件的清晰定义与复用,增强代码的可维护性;与 React、Vue 等框架结合,提供更佳的开发体验;适用于大型项目,优化代码结构和性能。随着 Web 技术的发展,TypeScript 的应用前景广阔,将继续引领 Web 开发的新趋势。
36 2
|
1月前
|
监控 安全 测试技术
如何在实际项目中应用Python Web开发的安全测试知识?
如何在实际项目中应用Python Web开发的安全测试知识?
29 4
|
1月前
|
中间件 Go API
Go语言中几种流行的Web框架,如Beego、Gin和Echo,分析了它们的特点、性能及适用场景,并讨论了如何根据项目需求、性能要求、团队经验和社区支持等因素选择最合适的框架
本文概述了Go语言中几种流行的Web框架,如Beego、Gin和Echo,分析了它们的特点、性能及适用场景,并讨论了如何根据项目需求、性能要求、团队经验和社区支持等因素选择最合适的框架。
77 1
|
1月前
|
JavaScript 前端开发 开发工具
web项目规范配置(husky、eslint、lint-staged、commit)
通过上述配置,可以确保在Web项目开发过程中自动进行代码质量检查和规范化提交。Husky、ESLint、lint-staged和Commitlint共同作用,使得每次提交代码之前都会自动检查代码风格和语法问题,防止不符合规范的代码进入代码库。这不仅提高了代码质量,还保证了团队协作中的一致性。希望这些配置指南能帮助你建立高效的开发流程。
46 5
|
1月前
|
前端开发 API 开发者
Python Web开发者必看!AJAX、Fetch API实战技巧,让前后端交互如丝般顺滑!
在Web开发中,前后端的高效交互是提升用户体验的关键。本文通过一个基于Flask框架的博客系统实战案例,详细介绍了如何使用AJAX和Fetch API实现不刷新页面查看评论的功能。从后端路由设置到前端请求处理,全面展示了这两种技术的应用技巧,帮助Python Web开发者提升项目质量和开发效率。
53 1