整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

Ktor - Kotlin Web 框架

Ktor - Kotlin Web 框架

一步了解 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改变respondText?了response当前的基础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 的文档和示例。

约风公寓|邓林林

家和居住者之间,确实存在着一种难以言喻却又紧密相连的纽带,这种关系微妙而深刻,仿佛每一寸空间、每一件家具都在诉说着居住者的故事,蕴藏着他们生活的点点滴滴与独特印记。


大家好,我是《意大利室内设计中文版+》的主理人意公子,专为设计师分享室内设计创新案例。我们致力于为设计师提供多元的设计美学,也让世界看到我们的设计实力。


让我们携手踏入这座简约风格的私宅,这是一处不仅融合了现代设计美学,更满载着居住者深情故事与温馨回忆的避风港。



设计师选择以“安静”为核心设计理念,旨在打造一个能让居住者彻底放松、享受宁静时光的居家环境。这种设计不仅是对外界喧嚣的隔绝,更是对内心平静的追求。

The designer chose "quiet" as the core design concept to create a home environment where residents can completely relax and enjoy a quiet time. This design is not only an isolation from the hustle and bustle of the outside world, but also a pursuit of inner peace.



“一家一人”的哲学理念,在设计中体现为对居住者个人情感的尊重与呵护。设计师通过精细的布局与选材,让家成为居住者精神世界的延伸,赋予生活以全新的意义和价值。

The philosophy of "one family, one person" is reflected in the design as respect and care for the personal feelings of the occupants. Through the fine layout and material selection, the designer makes the home an extension of the spiritual world of the occupants, giving life a new meaning and value.



在确保结构安全的前提下,设计师大胆拆除了室内的隔断墙,使原本分隔的空间得以自由流通,形成了一体化的开阔视野。这种布局不仅增强了空间的通透性和连贯性,还促进了家庭成员之间的交流与互动。

On the premise of ensuring the safety of the structure, the designer boldly removed the partition wall in the interior, so that the originally separated space could flow freely and form an integrated open vision. This layout not only enhances the transparency and coherence of the space, but also promotes communication and interaction between family members.


深木色的地板,经过特殊处理呈现出自然的做旧效果,每一道痕迹都仿佛在诉说着岁月的故事。这种材质的选择不仅增加了空间的质感与层次感,还让人感受到一种沉稳与宁静的气息。

The dark wood-colored flooring has been specially treated to give a natural aged effect, and every trace seems to tell the story of the years. This choice of material not only adds texture and layering to the space, but also makes people feel a sense of calm and tranquility.


客厅作为家庭活动的核心区域,设计师在保持简约风格的同时,也注重了家具的实用性与美观性。每一件家具都经过精心挑选与搭配,既符合人体工程学原理又兼具艺术美感。

As the core area of family activities, the living room is the designer's focus on the practicality and aesthetics of the furniture while maintaining a simple style. Each piece of furniture has been carefully selected and matched to be both ergonomic and artistic.


客厅区域还巧妙地利用了墙面壁龛和矮柜等收纳空间来减少杂物堆积保持空间整洁有序。

The living room area also makes clever use of storage spaces such as wall alcoves and low cabinets to reduce clutter accumulation and keep the space tidy and organized.


餐厨厅位于客厅的另一侧通过巧妙的夹角设计实现了空间的有效分隔。这种布局既保证了餐厨区域的独立性又避免了视线上的直接冲突保持了整体的和谐质感。

The dining and kitchen room is located on the other side of the living room, and the space is effectively divided through a clever angle design. This layout not only ensures the independence of the kitchen area, but also avoids direct conflict in sight, and maintains the overall harmonious texture.


在卧室的设计上设计师更加注重居住者的私密性和舒适度。卧室空间减少了不必要的装饰元素转而更专注于设计感的营造和实用功能的实现。

In the design of the bedroom, the designer pays more attention to the privacy and comfort of the occupants. The bedroom space reduces unnecessary decorative elements and focuses more on the creation of a sense of design and the realization of practical functions.

简约的设计哲学不仅体现在空间的布局和家具的选择上更渗透在居住者的生活方式和心态中。

The minimalist design philosophy is not only reflected in the layout of the space and the choice of furniture, but also permeates the lifestyle and mentality of the occupants.


设计师通过留白的手法为生活留下了足够的喘息空间让居住者能够在繁忙的生活中找到一片宁静的避风港。同时这种简约而不简单的设计风格也体现了居住者对生活的热爱和追求在平淡中诠释着幸福的真谛。

The designer leaves enough breathing space for life through the use of white space, so that residents can find a peaceful haven in their busy lives. At the same time, this simple but not simple design style also reflects the love and pursuit of life of the residents, and interprets the true meaning of happiness in the ordinary.

这套私宅以其简约而不失格调的设计风格赢得了居住者的喜爱和赞誉。整个空间充满了温馨和谐的氛围让人感受到家的温暖和幸福。

This private home has won the love and praise of its occupants for its simple yet stylish design. The whole space is filled with a warm and harmonious atmosphere, making people feel the warmth and happiness of home.

、PHP环境搭建的前提是 Apache HTTP Server (Apache 服务器)已经安装部署成功,并可以正常访问到服务器的主页面。Apache HTTP Server 的安装部署已经在上一篇讲解的很详细了,不清楚的可以点击“ Apache HTTP Servcer-Apache服务器下载与Windows系统下安装”查看具体操作。

2、PHP下载:

2.1、下载地址:http://php.net/downloads.php

2.1、版本选择:如果是与 Apache 搭配,建议选择 Thread Safe 版本;如果是与 CGI 或者 FAST-CGI 搭配,建议选择 Non Thread Safe 版本。

3、PHP环境搭建:

3.1、将下载的 .zip 压缩包解压到指定的安装目录,此处安装路径为:D:\solt\php_5.6.33。

3.2、将 PHP 的根目录下的 php.ini-development 或者 php.ini-production复制一份并改名为 php.ini,作为PHP的配置文件;

3.3、打开php.ini,修改配置信息:

说明:a、ini文件的注释是分号(英文分号),所以取消注释就删除分号;

b、${phphome}是PHP的根目录,即:D:\solt\php_5.6.33,配置文件中写成绝对路径。

3.3.1、修改扩展文件的路径:

; extension_dir="ext"

取消注释,或者改为:

extension_dir="${phphome}\ext";

注意:如果是与 Apache 搭配,建议将扩展文件路径改为绝对路径,因为有可能加载不了。

3.3.2、修改需要加载的扩展文件,下面代码是取消部分扩展程序的注释之后的代码:

extension=php_bz2.dll

extension=php_curl.dll

extension=php_fileinfo.dll

extension=php_gd2.dll

extension=php_gettext.dll

;extension=php_gmp.dll

;extension=php_intl.dll

;extension=php_imap.dll

;extension=php_interbase.dll

;extension=php_ldap.dll

extension=php_mbstring.dll

extension=php_exif.dll ; Must be after mbstring as it depends on it

extension=php_mysql.dll

extension=php_mysqli.dll

;extension=php_oci8_12c.dll ; Use with Oracle Database 12c Instant Client

extension=php_openssl.dll

;extension=php_pdo_firebird.dll

extension=php_pdo_mysql.dll

;extension=php_pdo_oci.dll

extension=php_pdo_odbc.dll

extension=php_pdo_pgsql.dll

extension=php_pdo_sqlite.dll

extension=php_pgsql.dll

;extension=php_shmop.dll

; The MIBS data available in the PHP distribution must be installed.

; See http://www.php.net/manual/en/snmp.installation.php

;extension=php_snmp.dll

extension=php_soap.dll

extension=php_sockets.dll

extension=php_sqlite3.dll

;extension=php_sybase_ct.dll

extension=php_tidy.dll

extension=php_xmlrpc.dll

extension=php_xsl.dll

3.3.3、设置默认的时区:

[Date]

; Defines the default timezone used by the date functions

; http://php.net/date.timezone 选择时区列表网址

date.timezone=Asia/Shanghai

3.3.4、设置 ssl :

[openssl]

; The location of a Certificate Authority (CA) file on the local filesystem

; to use when verifying the identity of SSL/TLS peers. Most users should

; not specify a value for this directive as PHP will attempt to use the

; OS-managed cert stores in its absence. If specified, this value may still

; be overridden on a per-stream basis via the "cafile" SSL stream context

; option.

openssl.cafile=cacert.pem

根据需求可以自己添加扩展。

3.4、修改 Apache24\conf\ 目录下的 httpd.conf 配置 Apache ,让 Apache 和 PHP 协同工作;

3.4.1、DocumentRoot 设置:

修改前:默认的是 Apache24 下的 htdocs 目录:

修改后:指定到自定义的路径,但是要记住这个路径。

3.4.2、修改默认的索引,以支持 PHP :

修改前:

# DirectoryIndex: sets the file that Apache will serve if a directory

# is requested.

#

<IfModule dir_module>

DirectoryIndex index.html

</IfModule>

修改后:

# DirectoryIndex: sets the file that Apache will serve if a directory

# is requested.

#

<IfModule dir_module>

DirectoryIndex index.html index.php index.htm

</IfModule>

3.4.3、开启 rewrite 功能:将下面这行代码前面的 # 去掉:

LoadModule rewrite_module modules/mod_rewrite.so

3.4.4、加载 PHP 模块,注意绝对路径:

如果是 PHP 5,在 httpd.conf 文件最后添加如下代码:

#php5.6

LoadModule php5_module D:/soft/php-5.6.33/php5apache2_4.dll

<IfModule php5_module>

PHPIniDir "D:/soft/php-5.6.33/"

AddType application/x-httpd-php .php

AddType application/x-httpd-php-source .phps

</IfModule>

如果是 PHP 7,则相应的更改,示例如下:

#php7

LoadModule php7_module D:/soft/php-7.x.x/php7apache2_4.dll

<IfModule php7_module>

PHPIniDir "D:/soft/php-7.x.x/"

AddType application/x-httpd-php .php

AddType application/x-httpd-php-source .phps

</IfModule>

3.5、如果安装的PHP x64位版本,Apache也需要是x64位版本的。然后还要将php目录下的libeay32.dll、ssleay32.dll、libssh2.dll以及ext目录下的php_curl.dll等四个文件,都复制放到System32目录下。否则curl扩展无法使用。参考链接:http://my.oschina.net/lsfop/blog/496181 。未做验证,只是照做,正常显示。

4、Apache + PHP 启动运行测试:

4.1、在 D:\soft\php_5.6.33\www 目录下,创建 index.php 文件,文本内容如下:

<?php

echo phpinfo();

?>

4.2、重启 Apache 服务,打开浏览地址栏输入: localhost:8081/index.php 或者 127.0.0.1:8081/index.php ,就可以打开 PHP 页面。

最后说明一点儿,PHP 环境搭建可以借助于 PHP 开发环境搭建工具,下载地址:http://www.php.cn/xiazai/gongju或者http://www.wampserver.com/,在此处就不介绍工具安装方法了。

原文:https://blog.csdn.net/weixin_39082031/article/details/79110311

相关内容:https://study.163.com/course/courseMain.htm?courseId=1210406206&share=2&shareId=480000002227524