最近在学kotlin,有些懵啊,学了基本也忘了点,工作很忙最近才好点,这句话是今天加上的,写这个博客写了好几天,一天只能写一点,没时间啊,我不是那种可以睡很少觉的人,我要是熬夜就不能保证第二天的工作质量了,烦啊。而且无奈业余时间又有限,真是很矛盾啊很尴尬啊,为了和大学同学住一个房子,每天上班要一个半小时,下班一个半小时。还好我女朋友送了我一个kindle(秀一下),可以在路上学习,不然我要疯啊。
不禁想起刘帅微信群里的一些大佬们,天天都在学习,一问他们在干吗,都在说公司最近闲得很,要跳槽之类的。。。。。。
他们怎么那么闲啊,有一位大佬,天天去打游戏,游戏打腻了,就去学学习,md这几天他又宣布了,公司成为了国企,o( ̄︶ ̄)o我说怎么那么闲呢。
kotlin可以写Android,IOS,也可以开发后端,利用kotlin成为一个全栈工程师,是十分简单的。于是就试着用kotlin搭建了一下后台服务,能搭起来,还不错哈哈哈哈。
idea安装ktor插件:
安装完了之后,开始利用插件创建项目:
File->new Project 就会看到这个画面:
1:你的jdk。
2:选择ktor插件。
3:选择你需要的一些特性。在这里不选择也行,到项目中可以利用gradle配置,等以后学到这再说这些都是干啥的。
4:同3。
点击next之后一路next+finish。
如果创建项目时没有选择3处的一些框架,创建项目完毕后,可以在gradle中添加,例如添加netty和logback:
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
compile "io.ktor:ktor-server-netty:$ktor_version"
compile "ch.qos.logback:logback-classic:1.2.1"
}
创建项目完毕后,看到如图所示结构:widget是我自己写的,忽略掉即可,正常创建之后没有。
application.conf文件:顾名思义,是一个配置文件,配置的modules既是application.kt中的module,如下:
如果新建的包含main的kt文件命名不是application.kt也没关系,只要和application.conf中的modules后面的名字一样即可。
在idea中,我们创建好的项目,会自动识别包含main方法的配置,可以直接运行起来。有的时候这个配置会报红,通常是module找不到的原因。我们如下图所示把module配置上即可。
1. 点击配置
2. 点击倒三角找到要运行的module
3. 点击他。配置完毕。
如果gradle中没有配置netty,那就配一下子:
compile "io.ktor:ktor-server-netty:$ktor_version"
compile "io.ktor:ktor-server-core:$ktor_version"
一些必不可少的,创建项目之后就会自己有的东西,检查一下:
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinx_coroutines_version"
compile "org.jetbrains.kotlinx:kotlinx-coroutines-io:$kotlinx_coroutines_version"
compile "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$kotlinx_coroutines_version"
以上配置通过之后,路由这个东东贼鸡儿简单。
在我们的fun Application.module() {}中写一个routing{},在routin中写一个普通的get方法:
routing {
get("/") {
call.respondText("HELLO WORLD!Ktor", contentType = ContentType.Text.Plain)
}
}
然后浏览器打开:http://0.0.0.0:8080/
(这里注意,如果你是自己利用插件建立的项目,建立好之后main方法是这样的:
fun main(args: Array): Unit = io.ktor.server.netty.DevelopmentEngine.main(args)
此时应该打开localhost的8080端口,才能看到结果。
如果你是和我一样,吧main改成了:
fun main(args: Array) {
embeddedServer(Netty, 8080, watchPaths = listOf("applicationKt"), module = Application::module).start()
}
那就打http://0.0.0.0:8080/。原因是embeddedServer中指定的host是0.0.0.0。
还可以在项目编译的时候,打印的日志中,找到Responding at xxxx 。这个xxxx就是你要打开的连接。
)
打开后看到结果:
改一下这部分文字:
注意左下,改了代码之后点击这个Rerun,不要点击项目右上角的run。不然则会告诉你端口已经被占用了哈哈哈。自己想想怎么回事。
rerun后结果:
好多教程都是用jackson。作为一个Android开发者,两个公司都用的gson。我自然对jackson没有好感。
1、配置gson:
配置这个gson有坑啊,这个gradle正确的连接找不到,不管用,后来在github的ktor官网上发现,应该是这个连接:
compile "io.ktor:ktor-gson:$ktor_version"
对应的github中的地址:
学习方法:所有ktor项目的特性,在ktor的github中都能找到。找到之后,看他的test怎么写的,你就怎么写。牛逼不?
2、gson返回map对象和接收map对象:
代码:还是在module里面写:
install(ContentNegotiation) {//引入我们需要的gson以及注册gsonconverter
register(ContentType.Application.Json, GsonConverter())
}
routing {
val model = mapOf("id" to 1, "title" to "Hello, World!", "unicode" to uc)
get("/gson") {
call.respond(model)
}
post("/gson") {
val map = call.receive
这里有个uc是:
val uc = "\u0422"
经过base64编码之后是个T。
写好代码之后rerun,浏览器中输入http://0.0.0.0:8080/gson
或者localhost的这个:8080/gson就会看到:
接收gson代码也简单,call.receive
接受一个map对象。
然后val text = map.entries.joinToString { "${it.key}=${it.value}" }
对其进行遍历并转换为string。
然后call.respond(text)
返回结果。
3、gson返回实体类对象。
定义实体类,kotlin就两行代码就搞定两个类,牛逼不?:
data class MyEntity(val id: Int, val name: String, val children: List<ChildEntity>)
data class ChildEntity(val item: String, val quantity: Int)
返回实体类:
routing {
val model = MyEntity(777, "Cargo", listOf(ChildEntity("Qube", 1), ChildEntity("Sphere", 2), ChildEntity(uc, 3)))
get("/gsonEntity") {
call.respond(model)
}
post("/gsonEntity") {
val entity = call.receive()
call.respond(entity.toString())
}
}
打开:http://0.0.0.0:8080/gsonEntity 查看返回结果:
freemarker就是用来。。。拷贝一下官方文档:
Apache FreeMarker™是一个模板引擎:一个Java库,用于根据模板和更改数据生成文本输出(HTML网页,电子邮件,配置文件,源代码等)。 模板是用FreeMarker模板语言(FTL)编写的,这是一种简单的专用语言(不像PHP这样的完整编程语言)。 通常,使用通用编程语言(如Java)来准备数据(发布数据库查询,进行业务计算)。 然后,Apache FreeMarker使用模板显示准备好的数据。 在模板中,您将关注如何呈现数据,而在模板之外,您将关注于要呈现的数据。
嗯,果然正经多了。
1、构建freemarker:
compile "io.ktor:ktor-freemarker:$ktor_version"
新建ftl文件:
这个ftl文件就和我们用过html一样。idea里支持ftl的格式,可以直接格式化代码。
2、编写页面:
<#-- @ftlvariable name="data" type="com.example.IndexData" -->
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
hahaha
<ul>
<#list data.items as item>
<li>${item}li>
#list>
ul>
body>
html>
3、返回页面
在module里面写:
install(FreeMarker) {//引入freemark并指定加载的文件夹
templateLoader = ClassTemplateLoader(this::class.java.classLoader, "templates")
}
routing {//返回index.ftl的内容,并
get("/freemarker") {
call.respond(FreeMarkerContent("index.ftl", mapOf("data" to IndexData(listOf(1, 2, 3))), ""))
}
}
data class IndexData(val items: List<Int>)
以上代码就是,我们先引入freemarker框架,然后返回index.ftl。并且传个参数data。
打开:http://0.0.0.0:8080/freemarker 查看结果:
开发html的时候,写完html可以直接浏览器预览,很方便,那ftl文件可以吗?
ftl文件也是可以写完之后马上看到结果的:
我们在ftl页面右击(mac双击),然后选择Recompile,就行了。
freemarker内部是利用管道原理和buffwriter实现的:我又得复习了:
package io.ktor.freemarker
import freemarker.template.*
import io.ktor.application.*
import io.ktor.cio.*
import io.ktor.content.*
import io.ktor.http.*
import io.ktor.response.*
import io.ktor.util.*
import kotlinx.coroutines.experimental.io.*
class FreeMarkerContent(val template: String,
val model: Any?,
val etag: String? = null,
val contentType: ContentType = ContentType.Text.Html.withCharset(Charsets.UTF_8))
class FreeMarker(val config: Configuration) {
companion object Feature : ApplicationFeature {
override val key = AttributeKey("freemarker")
override fun install(pipeline: ApplicationCallPipeline, configure: Configuration.() -> Unit): FreeMarker {
val config = Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS).apply(configure)
val feature = FreeMarker(config)
pipeline.sendPipeline.intercept(ApplicationSendPipeline.Transform) { value ->
if (value is FreeMarkerContent) {
val response = feature.process(value)
proceedWith(response)
}
}
return feature
}
}
private fun process(content: FreeMarkerContent): FreeMarkerOutgoingContent {
return FreeMarkerOutgoingContent(config.getTemplate(content.template), content.model, content.etag, content.contentType)
}
private class FreeMarkerOutgoingContent(val template: Template,
val model: Any?,
etag: String?,
override val contentType: ContentType) : OutgoingContent.WriteChannelContent() {
override suspend fun writeTo(channel: ByteWriteChannel) {
channel.bufferedWriter(contentType.charset() ?: Charsets.UTF_8).use {
template.process(model, it)
}
}
init {
if (etag != null)
versions += EntityTagVersion(etag)
}
}
}
1、还是老样子,配置gradle:其实这个可以在构建项目的时候,直接勾选的。
compile "io.ktor:ktor-html-builder:$ktor_version"
compile "org.jetbrains:kotlin-css-jvm:1.0.0-pre.31-kotlin-1.2.41"
2、敲代码,还是那个module:写个 routing {}
get("/html-dsl") {
call.respondHtml {
head { link(rel = "stylesheet", href = "/styles.css") }
body {
h1 { +"HTML" }
ul {
for (n in 1..10) {
li { +"$n" }
}
}
p { +"唉呀妈呀" }
}
}
}
get("/styles.css") {
call.respondCss {
body {
backgroundColor = Color("#aaccff")
}
p {
fontSize = 2.em
fontStyle = FontStyle("italic")
}
rule("p.myclass") {
color = Color.blue
}
}
}
以上代码就是i写一个html并且连接自己写的css。返回css方法:
suspend inline fun ApplicationCall.respondCss(builder: CSSBuilder.() -> Unit) {
this.respondText(CSSBuilder().apply(builder).toString(), ContentType.Text.CSS)
}
查看结果:
dsl的好处就是一旦运行起来之后,效率比html快。但是再快还能快到哪里去?我对官方给出的这个说法提出鄙视。
而且每次修改改完都要Rerun。还好官方给出了可以配置项目快速编译的方法,可以缓解一小下尴尬。
通常,重新启动服务器可能需要一些时间,因此Ktor提供了一个基本的自动重载工具,可以重新加载Application类。
ktor说的:
Autoreload在Java 9中不起作用。如果您想使用它,请立即坚持使用JDK 8。
使用自动重新加载时会有性能损失。 所以请记住,您不应该在生产中或在进行基准测试时使用它。
AutoReload的文档:
https://ktor.kotlincn.net/servers/autoreload.html#java9
看不懂我再来个翻译帖。明后天发。