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

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

ccwgpt 2025-05-02 09:27 35 浏览 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 的文档和示例。



相关推荐

一个基于.Net Core遵循Clean Architecture原则开源架构

今天给大家推荐一个遵循CleanArchitecture原则开源架构。项目简介这是基于Asp.netCore6开发的,遵循CleanArchitecture原则,可以高效、快速地构建基于Ra...

AI写代码翻车无数次,我发现只要提前做好这3步,bug立减80%

写十万行全是bug之后终于找到方法了开发"提示词管理助手"新版本那会儿,我差点被bug整崩溃。刚开始两周,全靠AI改代码架构,结果十万行程序漏洞百出。本来以为AI说没问题就稳了,结果...

OneCode低代码平台的事件驱动设计:架构解析与实践

引言:低代码平台的事件驱动范式在现代软件开发中,事件驱动架构(EDA)已成为构建灵活、松耦合系统的核心范式。OneCode低代码平台通过创新性的注解驱动设计,将事件驱动理念深度融入平台架构,实现了业务...

国内大厂AI插件评测:根据UI图生成Vue前端代码

在IDEA中安装大厂的AI插件,打开ruoyi增强项目:yudao-ui-admin-vue31.CodeBuddy插件登录腾讯的CodeBuddy后,大模型选择deepseek-v3,输入提示语:...

AI+低代码技术揭秘(二):核心架构

本文档介绍了为VTJ低代码平台提供支持的基本架构组件,包括Engine编排层、Provider服务系统、数据模型和代码生成管道。有关UI组件库和widget系统的信息,请参阅UI...

GitDiagram用AI把代码库变成可视化架构图

这是一个名为gitdiagram的开源工具,可将GitHub仓库实时转换为交互式架构图,帮助开发者快速理解代码结构。核心功能一键可视化:替换GitHubURL中的"hub...

30天自制操作系统:第六天:代码架构整理与中断处理

1.拆开bootpack.c文件。根据设计模式将对应的功能封装成独立的文件。2.初始化pic:pic(可编程中断控制器):在设计上,cpu单独只能处理一个中断。而pic是将8个中断信号集合成一个中断...

AI写代码越帮越忙?2025年研究揭露惊人真相

近年来,AI工具如雨后春笋般涌现,许多人开始幻想程序员的未来就是“对着AI说几句话”,就能轻松写出完美的代码。然而,2025年的一项最新研究却颠覆了这一期待,揭示了一个令人意外的结果。研究邀请了16位...

一键理解开源项目:两个自动生成GitHub代码架构图与说明书工具

一、GitDiagram可以一键生成github代码仓库的架构图如果想要可视化github开源项目:https://github.com/luler/reflex_ai_fast,也可以直接把域名替换...

5分钟掌握 c# 网络通讯架构及代码示例

以下是C#网络通讯架构的核心要点及代码示例,按协议类型分类整理:一、TCP协议(可靠连接)1.同步通信//服务器端usingSystem.Net.Sockets;usingTcpListene...

从复杂到优雅:用建造者和责任链重塑代码架构

引用设计模式是软件开发中的重要工具,它为解决常见问题提供了标准化的解决方案,提高了代码的可维护性和可扩展性,提升了开发效率,促进了团队协作,提高了软件质量,并帮助开发者更好地适应需求变化。通过学习和应...

低代码开发当道,我还需要学习LangChain这些框架吗?| IT杂谈

专注LLM深度应用,关注我不迷路前两天有位兄弟问了个问题:当然我很能理解这位朋友的担忧:期望效率最大化,时间用在刀刃上,“不要重新发明轮子”嘛。铺天盖地的AI信息轰炸与概念炒作,很容易让人浮躁与迷茫。...

框架设计并不是简单粗暴地写代码,而是要先弄清逻辑

3.框架设计3.框架设计本节我们要开发一个UI框架,底层以白鹭引擎为例。框架设计的第一步并不是直接撸代码,而是先想清楚设计思想,抽象。一个一个的UI窗口是独立的吗?不是的,...

大佬用 Avalonia 框架开发的 C# 代码 IDE

AvalonStudioAvalonStudio是一个开源的跨平台的开发编辑器(IDE),AvalonStudio的目标是成为一个功能齐全,并且可以让开发者快速使用的IDE,提高开发的生产力。A...

轻量级框架Lagent 仅需20行代码即可构建自己的智能代理

站长之家(ChinaZ.com)8月30日消息:Lagent是一个专注于基于LLM模型的代理开发的轻量级框架。它的设计旨在简化和提高这种模型下代理的开发效率。LLM模型是一种强大的工具,可以...

取消回复欢迎 发表评论: