如何使用 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月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
101 3
|
16天前
|
设计模式 前端开发 数据库
Python Web开发:Django框架下的全栈开发实战
【10月更文挑战第27天】本文介绍了Django框架在Python Web开发中的应用,涵盖了Django与Flask等框架的比较、项目结构、模型、视图、模板和URL配置等内容,并展示了实际代码示例,帮助读者快速掌握Django全栈开发的核心技术。
103 45
|
14天前
|
JavaScript 前端开发 开发工具
web项目规范配置(husky、eslint、lint-staged、commit)
通过上述配置,可以确保在Web项目开发过程中自动进行代码质量检查和规范化提交。Husky、ESLint、lint-staged和Commitlint共同作用,使得每次提交代码之前都会自动检查代码风格和语法问题,防止不符合规范的代码进入代码库。这不仅提高了代码质量,还保证了团队协作中的一致性。希望这些配置指南能帮助你建立高效的开发流程。
32 5
|
12天前
|
前端开发 API 开发者
Python Web开发者必看!AJAX、Fetch API实战技巧,让前后端交互如丝般顺滑!
在Web开发中,前后端的高效交互是提升用户体验的关键。本文通过一个基于Flask框架的博客系统实战案例,详细介绍了如何使用AJAX和Fetch API实现不刷新页面查看评论的功能。从后端路由设置到前端请求处理,全面展示了这两种技术的应用技巧,帮助Python Web开发者提升项目质量和开发效率。
27 1
|
15天前
|
XML 安全 PHP
PHP与SOAP Web服务开发:基础与进阶教程
本文介绍了PHP与SOAP Web服务的基础和进阶知识,涵盖SOAP的基本概念、PHP中的SoapServer和SoapClient类的使用方法,以及服务端和客户端的开发示例。此外,还探讨了安全性、性能优化等高级主题,帮助开发者掌握更高效的Web服务开发技巧。
|
17天前
|
安全 数据库 开发者
Python Web开发:Django框架下的全栈开发实战
【10月更文挑战第26天】本文详细介绍了如何在Django框架下进行全栈开发,包括环境安装与配置、创建项目和应用、定义模型类、运行数据库迁移、创建视图和URL映射、编写模板以及启动开发服务器等步骤,并通过示例代码展示了具体实现过程。
29 2
|
20天前
|
JavaScript 前端开发 数据安全/隐私保护
Web开发者必看:手把手教你如何轻松播放m3u8流地址,解锁视频播放新技能,让你的项目更上一层楼!
【10月更文挑战第23天】随着互联网技术的发展,m3u8格式因良好的兼容性和高压缩率被广泛用于网络流媒体传输。本文介绍如何在Web端播放m3u8流地址,包括引入视频播放器(如Video.js)、创建播放器容器、初始化播放器及播放m3u8流的具体步骤。此外,还涉及处理加密m3u8流的示例。
70 1
|
29天前
|
JSON 搜索推荐 API
Python的web框架有哪些?小项目比较推荐哪个?
【10月更文挑战第15天】Python的web框架有哪些?小项目比较推荐哪个?
48 1
WK
|
17天前
|
安全 Java 编译器
C++和Java哪个更适合开发web网站
在Web开发领域,C++和Java各具优势。C++以其高性能、低级控制和跨平台性著称,适用于需要高吞吐量和低延迟的场景,如实时交易系统和在线游戏服务器。Java则凭借其跨平台性、丰富的生态系统和强大的安全性,广泛应用于企业级Web开发,如企业管理系统和电子商务平台。选择时需根据项目需求和技术储备综合考虑。
WK
24 0
|
1月前
|
设计模式 测试技术 持续交付
开发复杂Web应用程序
【10月更文挑战第3天】开发复杂Web应用程序
38 2