百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Ktor - Kotlin Web 框架(kotlin orm框架)

ccwgpt 2025-05-02 09:27 28 浏览 0 评论

进一步了解 Ktor,一个 Kotlin Web 框架。



Ktor是一个为 Kotlin 编写和设计的异步 Web 框架,它利用协程并允许您编写异步代码,而无需自己管理任何线程。

这是有关 Ktor 的更多背景信息。它得到了Jetbrains的支持,Jetbrains也是 Kotlin 本身的创造者。有谁比开发 Kotlin 的人更适合制作 Kotlin Web 框架?

执行

依赖项

buildscript {
  ext.kotlin_version = '1.7.10'
  ext.ktor_version = '2.0.3'

  repositories {
    mavenCentral()
  }
  dependencies {
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
  }
}

apply plugin: 'java'
apply plugin: 'kotlin'

group 'ktor-and-kodein-di'
version '1.0.0'

repositories {
  mavenLocal()
  mavenCentral()
}

dependencies {
  implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
  testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
  testImplementation "junit:junit:4.12"

  implementation "io.ktor:ktor-server-netty:$ktor_version"
  // each plugin is its own dependency which matches the name in code (default-headers = DefaultHeaders)
  implementation "io.ktor:ktor-serialization-jackson:$ktor_version"
  implementation "io.ktor:ktor-server-default-headers:$ktor_version"
  implementation "io.ktor:ktor-server-call-logging:$ktor_version"
  implementation "io.ktor:ktor-server-content-negotiation:$ktor_version"

  implementation group: 'org.kodein.di', name: 'kodein-di-generic-jvm', version: '6.5.1'
  implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
  implementation group: 'com.datastax.oss', name: 'java-driver-core', version: '4.14.1'
}

这里正在发生一些事情。

  • Ktor2.0.3使用1.7我从他们的文档中确定的最低版本的 Kotlin。
  • 引入了依赖ktor-server-netty和几个ktor-server插件。正如ktor-server-netty建议的那样,本文将使用Netty 。根据您选择导入的内容,可以使用不同的底层 Web 服务器。其他可用选项包括 Netty、Jetty、Tomcat 和 CIO。更多信息可以在支持的引擎文档中找到。
  • 引入 Logback来处理日志记录。这不包含在 Ktor 依赖项中,如果您计划进行任何类型的日志记录,则需要它。
  • Kodein是一个用 Kotlin 编写的依赖注入框架。我在这篇文章中松散地使用了它,并且由于代码示例的大小,我可能会完全删除它。

启动 Web 服务器

摆脱了无聊的东西,我现在可以让你完成一个 Web 服务器的实现。下面的代码就是你所需要的:

kotlin

import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty

fun main() {
  embeddedServer(Netty, port = 8080, module = Application::module).start()
}

fun Application.module() {
  // code that does stuff which is covered later
}

巴姆。你有它。使用 Ktor 和 Netty 运行的 Web 服务器。好的,是的,它并没有真正做任何事情,但我们将在以下部分中对此进行扩展。

该代码非常不言自明。唯一值得强调的是Application.module功能。该module参数embeddedServer需要Application.() -> Unit提供一个函数来配置服务器,并将成为服务器代码的主要入口点。

在接下来的部分中,我们将扩展内容Application.module,以便您的 Web 服务器真正做一些有价值的事情。

路由

所有传入的请求都将被拒绝,因为没有端点来处理它们。通过设置路由,您可以指定请求可以通过的有效路径以及在请求到达目的地时将处理请求的函数。

这是在一个Routing块(或多个Routing块)内完成的。在块内部,设置了到不同端点的路由:

import io.ktor.http.HttpStatusCode
import io.ktor.server.application.call
import io.ktor.server.request.receive
import io.ktor.server.response.respond
import io.ktor.server.response.respondText
import io.ktor.server.routing.Routing
import io.ktor.server.routing.get
import io.ktor.server.routing.post
import io.ktor.server.routing.route

routing {
  // All routes defined inside are prefixed with "/people"
  route("/people") {
    // Get a person
    get("/{id}") {
      val id = UUID.fromString(call.parameters["id"]!!)
      personRepository.find(id)?.let {
        call.respond(HttpStatusCode.OK, it)
      } ?: call.respondText(status = HttpStatusCode.NotFound) { "There is no record with id: $id" }
    }
    // Create a person
    post {
      val person = call.receive<Person>()
      val result = personRepository.save(person.copy(id = UUID.randomUUID()))
      call.respond(result)
    }
  }
}

由于依赖于扩展函数,导入已包含在内,这使得在没有 IDE 的情况下发现函数变得困难。

routing是一个方便的函数,可以让代码流畅。里面的上下文(又名thisrouting是类型Routing。此外,函数routegetpost都是 的扩展函数Routing

route设置其所有后续端点的基本路径。在这种情况下,/people. get并且post不要自己指定路径,因为基本路径足以满足他们的需求。如果需要,可以为每个路径添加一个路径,例如:

routing {
  // Get a person
  get("/people/{id}") {
    val id = UUID.fromString(call.parameters["id"]!!)
    personRepository.find(id)?.let {
      call.respond(HttpStatusCode.OK, it)
    } ?: call.respondText(status = HttpStatusCode.NotFound) { "There is no record with id: $id" }
  }
  // Create a person
  post("/people) {
    val person = call.receive<Person>()
    val result = personRepository.save(person.copy(id = UUID.randomUUID()))
    call.respond(result)
  }
}

在继续下一节之前,我想向您展示我是如何实际实现路由的:

fun Application.module() {
  val personRepository by kodein.instance<PersonRepository>()
  // Route requests to handler functions
  routing { people(personRepository) }
}

// Extracted to a separate extension function to tidy up the code
fun Routing.people(personRepository: PersonRepository) {
  route("/people") {
    // Get a person
    get("/{id}") {
      val id = UUID.fromString(call.parameters["id"]!!)
      personRepository.find(id)?.let {
        call.respond(HttpStatusCode.OK, it)
      } ?: call.respondText(status = HttpStatusCode.NotFound) { "There is no record with id: $id" }
    }
    // Create a person
    post {
      val person = call.receive<Person>()
      val result = personRepository.save(person.copy(id = UUID.randomUUID()))
      call.respond(result)
    }
  }
}

我将代码提取到一个单独的函数中以减少Application.module. 在尝试编写更重要的应用程序时,这将是一个好主意。我的方式是否是Ktor方式是另一个问题。快速浏览一下 Ktor 文档,看起来这是一个不错的解决方案。我相信我看到了另一种方法来做到这一点,但我需要花更多的时间来处理它。

请求处理程序的内容

将请求路由到请求处理程序时执行的代码显然非常重要。该功能毕竟需要做一些事情......

每个处理函数都在协程的上下文中执行。我并没有真正使用这个事实,因为我展示的每个功能都是完全同步的。

在本文的其余部分,我将尽量不过多提及协程,因为它们对于这个简单的 REST API 并不是特别重要。

在本节中,get将更仔细地检查该函数:


get("/{id}") {
  val id = UUID.fromString(call.parameters["id"]!!)
  personRepository.find(id)?.let {
    call.respond(HttpStatusCode.OK, it)
  } ?: call.respondText(status = HttpStatusCode.NotFound) { "There is no record with id: $id" }
}

{id}表示请求中需要路径变量,其值将存储为id. 可以包含多个路径变量,但本示例只需要一个。该值id是从 中检索的call.parameters,它采用您要访问的变量的名称。

  • call表示当前请求的上下文。
  • parameters是请求参数的列表。

id数据库使用路径变量搜索相应的记录 。在这种情况下,如果存在,则记录与相应的200 OK. 如果不是,则返回错误响应。两者都respond改变respondTextresponse当前的基础call。您可以手动执行此操作,例如,使用:


call.response.status(HttpStatusCode.OK)
call.response.pipeline.execute(call, it)

你可以这样做,但没有任何必要,因为这实际上只是respond. respondText有一些额外的逻辑,但委托response给最终确定一切。此函数中的最终调用 execute表示函数的返回值。

安装插件

在 Ktor 中,可以在需要时安装插件。例如,可以添加Jackson JSON 解析以从应用程序处理和返回 JSON。

以下是安装到示例应用程序的插件:

import io.ktor.http.HttpHeaders
import io.ktor.serialization.jackson.jackson
import io.ktor.server.application.install
import io.ktor.server.plugins.callloging.CallLogging
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.plugins.defaultheaders.DefaultHeaders
import org.slf4j.event.Level

fun Application.module() {
  // Adds header to every response
  install(DefaultHeaders) { header(HttpHeaders.Server, "My ktor server") }
  // Controls what level the call logging is logged to
  install(CallLogging) { level = Level.INFO }
  // Setup jackson json serialisation
  install(ContentNegotiation) { jackson() }
}
  • DefaultHeaders使用服务器名称为每个响应添加一个标头。
  • CallLogging记录有关传出响应的信息并指定记录它们的级别。需要包含一个日志库才能使其工作。输出将如下所示:

INFO ktor.application.log - 200 OK: GET - /people/302a1a73-173b-491c-b306-4d95387a8e36

  • ContentNegotiation告诉服务器使用 Jackson 处理传入和传出请求。请记住,这需要包括ktor-serialization-jackson作为依赖项。如果您愿意,也可以使用GSON。

对于 Ktor 包含的其他插件的列表,您可以转到start.ktor.io,您可以在其中查看现有插件(通过假装创建新应用程序)。

安装插件一直与之前完成的路由联系在一起。routing委托到install其实现内部。所以你可以写:


install(Routing) {
  route("/people") {
    get {
      // Implementation
    }
  }
}

无论你的船漂浮什么,但我会坚持使用routing. 希望这可以帮助您了解幕后发生的事情,即使只是一点点。

Kodein简介

我想简要介绍一下Kodein,因为我在这篇文章中使用了它。Kodein 是一个用 Kotlin 编写的依赖注入框架,适用于 Kotlin。下面是我用于示例应用程序的超少量 DI:

科特林

val kodein = Kodein {
  bind<CqlSession>() with singleton { cassandraSession() }
  bind<PersonRepository>() with singleton { PersonRepository(instance()) }
}
val personRepository by kodein.instance<PersonRepository>()

Kodein块内,创建了应用程序类的实例。在这种情况下,每个类只需要一个实例。呼召singleton表明了这一点。instance是 Kodein 提供的用于传递给构造函数而不是实际对象的占位符吗?

在块之外,检索Kodein一个实例。PersonRespository

是的,我知道; 在这里使用 Kodein 没有多大意义,因为我可以用一行代码替换它……

val  personRepository  =  PersonRepository ( cassandraSession ())

相反,让我们认为这是一个非常简洁的例子来理解。

概括

在这篇文章中,我们研究了使用 Ktor 初始化 Web 服务器,将请求路由到生成响应的 lambdas/handlers,以及将插件安装到服务器。在这篇文章中,我们主要停留在表面,专注于基础知识,让您开始使用 Ktor。有关更多信息,值得前往ktor.io并查看 Ktor 的文档和示例。



相关推荐

用Deepseek扩写土木工程毕业论文实操指南

用Deepseek扩写毕业论文实操指南一、前期准备整理现有论文初稿/提纲列清楚论文核心框架(背景、现状、意义、方法、数据、结论等)梳理好关键文献,明确核心技术路线二、Deepseek扩写核心思路...

985学霸亲授,DeepSeek也能绘6大科研图表,5分钟就出图

在实验数据处理中,高效可视化是每个科研人的必修课。传统绘图软件操作复杂、耗时费力,而智能工具DeepSeek的出现彻底改变了这一现状。本文将详解如何用DeepSeek一键生成六大科研常用图表,从思维导...

AI写论文刷屏?大学生正在丢掉的思考力

一、宿舍深夜:当论文变成"Ctrl+C+V"凌晨两点的大学宿舍,小王对着电脑屏幕叹气。本该三天前开始写的近代史论文,此刻还一片空白。他熟练打开某AI写作网站,输入"论五四运动的...

Grok在辅助论文写作上能不能既“聪明”又“可怕”?!

AcademicIdeas-学境思源AI初稿写作随着人工智能技术的飞速发展,论文写作这一学术任务正迎来新的助力。2025年2月18日,美国xAI公司推出了备受瞩目的Grok3模型,其创始人埃隆·...

大四论文沟通场景!音频转文字难题听脑AI来化解

大四学生都知道,写论文时和导师沟通修改意见,简直是“过关斩将”。电话、语音沟通完,想把导师说的修改方向、重点要求记下来,麻烦事儿可不少。手写记不全,用普通录音转文字工具,转完还得自己慢慢找重点,稍不注...

论文写作 | 技术路线图怎么画?(提供经典优秀模板参考)

技术路线图是一种图表或文字说明,用于描述研究目标、方法和实施计划。它展示了研究的整体框架和步骤,有助于读者理解研究的逻辑和进展。在课题及论文中,技术路线图是常见的一部分,甚至是一个类似心脏一样的中枢器...

25年信息系统项目管理师考试第2批论文题目写作建议思路框架

25年信息系统项目管理师考试第2批论文题目写作建议思路框架--马军老师

微信购物应尽快纳入法律框架(微信购物管辖)

符向军近日,甘肃省工商行政管理局发布《2016年上半年信息分析报告》。报告显示,微信网购纠纷迅猛增长,网络购物投诉呈上升趋势。投诉的主要问题有出售的商品质量不过关、消费者通过微信付款后对方不发货、购买...

泛珠三角区域网络媒体与腾讯微信签署《战略合作框架协议》

新海南客户端、南海网7月14日消息(记者任桐)7月14日上午,参加第四届泛珠三角区域合作网络媒体论坛的区域网络媒体负责人及嘉宾一行到腾讯微信总部座谈交流,并签署《战略合作框架协议》(以下简称《框架协...

离线使用、植入微信-看乐心Mambo手环如何打破框架

从2014年开始智能手环就成功进入人们的生活,至今已经演变出数据监测、信息推送、心率监测等诸多五花八门的功能,人们选择智能手环并不指望其能够改变身体健康情况,更多的是通过数据来正视自身运动情况和身体健...

微信私域电商运营策略与框架(微信私域怎么做)

...

华专网络:如何零基础制作一个网站出来?

#如何零基础制作一个网站出来?#你是不是觉得网站建设很复杂,觉得自己是小白,需求不明确、流程搞不懂、怕被外包公司坑……这些问题我都懂!今天华专网络就用大白话给你捋清楚建站的全流程,让你轻松get网站制...

WAIC2024丨明日上午9点,不见不散!共同探讨智能社会与全球治理框架

大咖云集,硕果闪耀WAIC2024世界人工智能大会智能社会论坛将于7月5日9:00-12:00与你相约直播间WAIC2024上海杨浦同济大学哔哩哔哩多平台同步直播探讨智能社会与全球治理框架WAIC...

约基奇:森林狼换来戈贝尔时大家都在嘲笑 他们的阵容框架很不错

直播吧5月4日讯西部季后赛半决赛,掘金将迎战森林狼,约基奇赛前接受采访。约基奇说道:“当蒂姆-康纳利(森林狼总经理、前掘金总经理&曾选中约基奇)做了那笔交易(换来戈贝尔)时,每个人都在嘲笑他...

视频号带货为什么一个流量都没有?顶级分析框架送给你

视频号带货为什么一个流量都没有?遇到问题,一定是步步来分析内容,视频号带货一个流量都没有,用另外一个意思来讲,就可以说是零播放。为什么视频号带货一个流量都没有?跟你说再多,都不如来个分析框架。1、是否...

取消回复欢迎 发表评论: