ktor - bkug.by

84
Ktor Ruslan Ibragimov / ibragimov.by

Upload: others

Post on 24-Feb-2022

17 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Ktor - bkug.by

KtorRuslan Ibragimov / ibragimov.by

Page 2: Ktor - bkug.by

Ktor

2/85

Page 3: Ktor - bkug.by

Конкуренты

3/85

VS

Page 4: Ktor - bkug.by

Конкуренты

4/85

VS

Page 5: Ktor - bkug.by

Можно использовать с Kotlin

5/85

Page 6: Ktor - bkug.by

Можно использовать с Kotlin

6/85

Page 7: Ktor - bkug.by

Асинхронные

7/85

Page 8: Ktor - bkug.by

Асинхронные

8/85

Page 9: Ktor - bkug.by

Корутины

9/85

Page 10: Ktor - bkug.by

Корутины

10/85

Page 11: Ktor - bkug.by

Конкуренты

11/85

VS

Page 12: Ktor - bkug.by

12/85

One more thing...

Page 13: Ktor - bkug.by

Мультиплатформа

13/85

API

Page 14: Ktor - bkug.by

Java, Java, Java-Java Jing-Jing-Jing

14/85

API

Page 15: Ktor - bkug.by

Hello, World!

15/85

Page 16: Ktor - bkug.by

Gradle

16/85

buildscript { repositories { jcenter() }

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

Page 17: Ktor - bkug.by

Gradle

17/85

apply plugin: "kotlin"

compileKotlin { kotlinOptions.jvmTarget = "1.8"}

compileTestKotlin { kotlinOptions.jvmTarget = "1.8"}

kotlin.experimental.coroutines = "enable"

Page 18: Ktor - bkug.by

Gradle

18/85

repositories { jcenter() maven { url "https://dl.bintray.com/kotlin/kotlinx" } maven { url "https://dl.bintray.com/kotlin/ktor" }}

dependencies { compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion") compile("io.ktor:ktor-server-netty:$ktorVersion")}

Page 19: Ktor - bkug.by

Mainfun main(args: Array<String>) { embeddedServer(Netty, 8080) { routing { get("/") { call.respondText("I am Groot!", ContentType.Text.Html) } } }.start(wait = true)}

19/85

Page 20: Ktor - bkug.by

Mainfun main(args: Array<String>) { embeddedServer(Netty, 8080) { routing { get("/") { call.respondText("I am Groot!", ContentType.Text.Html) } } }.start(wait = true)}

20/85

Page 21: Ktor - bkug.by

Mainfun main(args: Array<String>) { embeddedServer(Netty, 8080) { routing { get("/") { call.respondText("I am Groot!", ContentType.Text.Html) } } }.start(wait = true)}

21/85

Page 22: Ktor - bkug.by

Mainfun main(args: Array<String>) { embeddedServer(Netty, 8080) { routing { get("/") { call.respondText("I am Groot!", ContentType.Text.Html) } } }.start(wait = true)}

22/85

Page 23: Ktor - bkug.by

Mainfun main(args: Array<String>) { embeddedServer(Netty, 8080) { routing { get("/") { call.respondText("I am Groot!", ContentType.Text.Html) } } }.start(wait = true)}

23/85

Page 24: Ktor - bkug.by

Mainfun main(args: Array<String>) { embeddedServer(Netty, 8080) { routing { get("/") { call.respondText("I am Groot!", ContentType.Text.Html) } } }.start(wait = true)}

24/85

Page 25: Ktor - bkug.by

Application::classfun main(args: Array<String>) { embeddedServer(Netty, 8080) { this: Application routing { get("/") { call.respondText("I am Groot!", ContentType.Text.Html) } } }.start(wait = true)}

25/85

Page 26: Ktor - bkug.by

Application::classfun main(args: Array<String>) { embeddedServer(Netty, 8080) { this: Application this.routing { get("/") { call.respondText("I am Groot!", ContentType.Text.Html) } } }.start(wait = true)}

26/85

Page 27: Ktor - bkug.by

Application::classApplication это Pipeline (точнее несколько Pipeline'ов)

27/85

Page 28: Ktor - bkug.by

Application::classApplication это Pipeline (точнее несколько Pipeline'ов)

28/85

ApplicationCall ApplicationCall

Pipeline

CheckHeaders

SetStatus

SerializeObject

Page 29: Ktor - bkug.by

Application::classApplication это Pipeline (точнее несколько Pipeline'ов)

29/85

ApplicationCall ApplicationCall

Pipeline

CheckHeaders

SetStatus

SerializeObject

Interceptors

Page 30: Ktor - bkug.by

PipelinePhase

30/85

ApplicationCall ApplicationCall

Application : ApplicationCallPipeline

Infrastructure Call Fallback

Page 31: Ktor - bkug.by

Netty ChannelPipeline

31/85

Page 32: Ktor - bkug.by

Servlet

32/85

Page 33: Ktor - bkug.by

PipelinesApplicationCallPipeline

● ApplicationReceivePipeline

● ApplicationSendPipeline

33/85

Page 34: Ktor - bkug.by

ApplicationCallPipeline (ApplicationCall)

● ApplicationReceivePipeline (ApplicationReceiveRequest, ApplicationCall, IncomingContent)

● ApplicationSendPipeline (ApplicationCall, OutgoingContent)

34/85

Pipelines

Page 35: Ktor - bkug.by

Application::classfun main(args: Array<String>) { embeddedServer(Netty, 8080) { this: Application this.routing { get("/") { call.respondText("I am Groot!", ContentType.Text.Html) } } }.start(wait = true)}

35/85

Page 36: Ktor - bkug.by

Application.interceptfun main(args: Array<String>) { embeddedServer(Netty, 8081) { intercept(ApplicationCallPipeline.Infrastructure) { // log request headers call.request.headers .forEach { name, values -> println("$name: ${values.joinToString()}") } }

//... }.start(wait = true)}

36/85

Page 37: Ktor - bkug.by

Application.interceptfun main(args: Array<String>) { embeddedServer(Netty, 8081) { intercept(ApplicationCallPipeline.Infrastructure) { // log request headers call.request.headers .forEach { name, values -> println("$name: ${values.joinToString()}") } }

//... }.start(wait = true)}

37/85

Page 38: Ktor - bkug.by

Application.interceptfun main(args: Array<String>) { embeddedServer(Netty, 8081) { intercept(ApplicationCallPipeline.Infrastructure) { // log request headers call.request.headers .forEach { name, values -> println("$name: ${values.joinToString()}") } }

//... }.start(wait = true)}

38/85

Page 39: Ktor - bkug.by

ApplicationCall::classfun main(args: Array<String>) { embeddedServer(Netty, 8081) { intercept(ApplicationCallPipeline.Infrastructure) { // log request headers call.request.headers .forEach { name, values -> println("$name: ${values.joinToString()}") } }

//... }.start(wait = true)}

39/85

Page 40: Ktor - bkug.by

ApplicationCall::classApplicationCall

● ApplicationRequest● ApplicationResponse● Attributes

40/85

Page 41: Ktor - bkug.by

Application

41/85

ApplicationCall ApplicationCall

ApplicationCallPipeline

Infrastructure Call Fallback

Page 42: Ktor - bkug.by

Feature● Роутинг● Аунтентификация● Логирование запросов● Проставление заголовков● CORS● Метрики● Сессии● и т.д.● см. ApplicationFeature

42/85

Page 43: Ktor - bkug.by

Featurerouting { get("/") { call.respondText("I am Groot!"), ContentType.Text.Html) }}

43/85

Page 44: Ktor - bkug.by

Featureinstall(Routing) { get("/") { call.respondText("I am Groot!", ContentType.Text.Html) }}

44/85

Page 45: Ktor - bkug.by

Featurefun main(args: Array<String>) { embeddedServer(Netty, 8081) { install(DefaultHeaders) install(CallLogging)

//.. }.start(wait = true)}

45/85

Page 46: Ktor - bkug.by

Featuresintercept(ApplicationCallPipeline.Infrastructure) { // log request headers call.request.headers .forEach { name, values -> println("$name: ${values.joinToString()}") }}

46/85

Page 47: Ktor - bkug.by

Featuresclass HeaderLoggingFeature(configuration: Configuration) { val exclusions = configuration.exclusions

class Configuration { var exclusions: List<String> = listOf() }

fun log(call: ApplicationCall) { call.request.headers .filter { name, _ -> !exclusions.contains(name) } .forEach { name, values -> println("$name: ${values.joinToString()}") } }

companion object Feature : ApplicationFeature<ApplicationCallPipeline, HeaderLoggingFeature.Configuration, HeaderLoggingFeature> { override val key = AttributeKey<HeaderLoggingFeature>("HeaderLoggingFeature")

override fun install(pipeline: ApplicationCallPipeline, configure: Configuration.() -> Unit): HeaderLoggingFeature { val configuration = HeaderLoggingFeature.Configuration().apply(configure) val feature = HeaderLoggingFeature(configuration)

pipeline.intercept(ApplicationCallPipeline.Infrastructure) { feature.log(call) } return feature } }}

47/85

Page 48: Ktor - bkug.by

Featuresclass HeaderLoggingFeature(configuration: Configuration) { val exclusions = configuration.exclusions

class Configuration { var exclusions: List<String> = listOf() }

fun log(call: ApplicationCall) { call.request.headers .filter { name, _ -> !exclusions.contains(name) } .forEach { name, values -> println("$name: ${values.joinToString()}") } }

companion object Feature : ApplicationFeature<ApplicationCallPipeline, HeaderLoggingFeature.Configuration, HeaderLoggingFeature> { override val key = AttributeKey<HeaderLoggingFeature>("HeaderLoggingFeature")

override fun install(pipeline: ApplicationCallPipeline, configure: Configuration.() -> Unit): HeaderLoggingFeature { val configuration = HeaderLoggingFeature.Configuration().apply(configure) val feature = HeaderLoggingFeature(configuration)

pipeline.intercept(ApplicationCallPipeline.Infrastructure) { feature.log(call) } return feature } }} 48/85

Page 49: Ktor - bkug.by

Featuresclass HeaderLoggingFeature(configuration: Configuration) { val exclusions = configuration.exclusions

class Configuration { var exclusions: List<String> = listOf() }

fun log(call: ApplicationCall) { call.request.headers .filter { name, _ -> !exclusions.contains(name) } .forEach { name, values -> println("$name: ${values.joinToString()}") } }

companion object Feature : ApplicationFeature<ApplicationCallPipeline, HeaderLoggingFeature.Configuration, HeaderLoggingFeature> { override val key = AttributeKey<HeaderLoggingFeature>("HeaderLoggingFeature")

override fun install(pipeline: ApplicationCallPipeline, configure: Configuration.() -> Unit): HeaderLoggingFeature { val configuration = HeaderLoggingFeature.Configuration().apply(configure) val feature = HeaderLoggingFeature(configuration)

pipeline.intercept(ApplicationCallPipeline.Infrastructure) { feature.log(call) } return feature } }} 49/85

Page 50: Ktor - bkug.by

Featuresclass HeaderLoggingFeature(configuration: Configuration) { val exclusions = configuration.exclusions

class Configuration { var exclusions: List<String> = listOf() }

fun log(call: ApplicationCall) { call.request.headers .filter { name, _ -> !exclusions.contains(name) } .forEach { name, values -> println("$name: ${values.joinToString()}") } }

companion object Feature : ApplicationFeature<ApplicationCallPipeline, HeaderLoggingFeature.Configuration, HeaderLoggingFeature> { override val key = AttributeKey<HeaderLoggingFeature>("HeaderLoggingFeature")

override fun install(pipeline: ApplicationCallPipeline, configure: Configuration.() -> Unit): HeaderLoggingFeature { val configuration = HeaderLoggingFeature.Configuration().apply(configure) val feature = HeaderLoggingFeature(configuration)

pipeline.intercept(ApplicationCallPipeline.Infrastructure) { feature.log(call) } return feature } }} 50/85

Page 51: Ktor - bkug.by

Featuresclass HeaderLoggingFeature(configuration: Configuration) { val exclusions = configuration.exclusions

class Configuration { var exclusions: List<String> = listOf() }

fun log(call: ApplicationCall) { call.request.headers .filter { name, _ -> !exclusions.contains(name) } .forEach { name, values -> println("$name: ${values.joinToString()}") } }

companion object Feature : ApplicationFeature<ApplicationCallPipeline, HeaderLoggingFeature.Configuration, HeaderLoggingFeature> { override val key = AttributeKey<HeaderLoggingFeature>("HeaderLoggingFeature")

override fun install(pipeline: ApplicationCallPipeline, configure: Configuration.() -> Unit): HeaderLoggingFeature {

val configuration = HeaderLoggingFeature.Configuration().apply(configure) val feature = HeaderLoggingFeature(configuration)

pipeline.intercept(ApplicationCallPipeline.Infrastructure) { feature.log(call) } return feature } }}

51/85

Page 52: Ktor - bkug.by

Featuresclass HeaderLoggingFeature(configuration: Configuration) { val exclusions = configuration.exclusions

class Configuration { var exclusions: List<String> = listOf() }

fun log(call: ApplicationCall) { call.request.headers .filter { name, _ -> !exclusions.contains(name) } .forEach { name, values -> println("$name: ${values.joinToString()}") } }

companion object Feature : ApplicationFeature<ApplicationCallPipeline, HeaderLoggingFeature.Configuration, HeaderLoggingFeature> { override val key = AttributeKey<HeaderLoggingFeature>("HeaderLoggingFeature")

override fun install(pipeline: ApplicationCallPipeline, configure: Configuration.() -> Unit): HeaderLoggingFeature {

val configuration = HeaderLoggingFeature.Configuration().apply(configure) val feature = HeaderLoggingFeature(configuration)

pipeline.intercept(ApplicationCallPipeline.Infrastructure) { feature.log(call) } return feature } }}

52/85

Page 53: Ktor - bkug.by

Featuresclass HeaderLoggingFeature(configuration: Configuration) { val exclusions = configuration.exclusions

class Configuration { var exclusions: List<String> = listOf() }

fun log(call: ApplicationCall) { call.request.headers .filter { name, _ -> !exclusions.contains(name) } .forEach { name, values -> println("$name: ${values.joinToString()}") } } companion object Feature : ApplicationFeature<ApplicationCallPipeline, HeaderLoggingFeature.Configuration, HeaderLoggingFeature> { override val key = AttributeKey<HeaderLoggingFeature>("HeaderLoggingFeature")

override fun install(pipeline: ApplicationCallPipeline, configure: Configuration.() -> Unit): HeaderLoggingFeature { val configuration = HeaderLoggingFeature.Configuration().apply(configure) val feature = HeaderLoggingFeature(configuration)

pipeline.intercept(ApplicationCallPipeline.Infrastructure) { feature.log(call) } return feature } }}

53/85

Page 54: Ktor - bkug.by

Featuresintercept(ApplicationCallPipeline.Infrastructure) { // log request headers call.request.headers .forEach { name, values -> println("$name: ${values.joinToString()}") }}

install(HeaderLoggingFeature)

install(HeaderLoggingFeature) { exclusions = listOf("User-Agent")}

54/85

Page 55: Ktor - bkug.by

Featuresintercept(ApplicationCallPipeline.Infrastructure) { // log request headers call.request.headers .forEach { name, values -> println("$name: ${values.joinToString()}") }}

install(HeaderLoggingFeature)

install(HeaderLoggingFeature) { exclusions = listOf("User-Agent")}

55/85

Page 56: Ktor - bkug.by

Featuresintercept(ApplicationCallPipeline.Infrastructure) { // log request headers call.request.headers .forEach { name, values -> println("$name: ${values.joinToString()}") }}

install(HeaderLoggingFeature)

install(HeaderLoggingFeature) { exclusions = listOf("User-Agent")}

56/85

Page 57: Ktor - bkug.by

Application и организация кодаfun main(args: Array<String>) { embeddedServer(Netty, 8080) { routing { get("/") { call.respondText("I am Groot!", ContentType.Text.Html) } } }.start(wait = true)}

57/85

Page 58: Ktor - bkug.by

Application и организация кодаfun main(args: Array<String>) { embeddedServer(Netty, 8080) { this: Application this.routing { get("/") { call.respondText("I am Groot!", ContentType.Text.Html) } } }.start(wait = true)}

58/85

Page 59: Ktor - bkug.by

Application и организация кодаfun Application.myApp() { routing { get("/") { call.respondText("I am Groot!", ContentType.Text.Html) } }}

fun main(args: Array<String>) { embeddedServer(Netty, 8081) { myApp() }.start(wait = true)}

59/85

Page 60: Ktor - bkug.by

Application и организация кодаfun Application.myApp() { routing { get("/") { call.respondText("I am Groot!", ContentType.Text.Html) } }}

fun main(args: Array<String>) { embeddedServer(Netty, 8081) { myApp() }.start(wait = true)}

60/85

Page 61: Ktor - bkug.by

Application и организация кодаfun Application.myApp() { routing { get("/") { call.respondText("I am Groot!", ContentType.Text.Html) } }}

fun main(args: Array<String>) { embeddedServer(Netty, 8081) { myApp() }.start(wait = true)}

61/85

Page 62: Ktor - bkug.by

Тестированиеdependencies { compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion") compile("io.ktor:ktor-server-netty:$ktorVersion")

compile("ch.qos.logback:logback-classic:1.2.1")

testCompile("junit:junit:4.12") testCompile("io.ktor:ktor-server-test-host:$ktorVersion")}

62/85

Page 63: Ktor - bkug.by

Тестированиеclass AppKtTest { @Test fun testIsIAmGroot() { withTestApplication(Application::myApp) { with(handleRequest(HttpMethod.Get, "/")) { assertEquals(HttpStatusCode.OK, response.status()) assertEquals("I am Groot!", response.content) } } }}

63/85

Page 64: Ktor - bkug.by

Тестированиеclass AppKtTest { @Test fun testIsIAmGroot() { withTestApplication(Application::myApp) { with(handleRequest(HttpMethod.Get, "/")) { assertEquals(HttpStatusCode.OK, response.status()) assertEquals("I am Groot!", response.content) } } }}

64/85

Page 65: Ktor - bkug.by

Тестированиеclass AppKtTest { @Test fun testIsIAmGroot() { withTestApplication(Application::myApp) { with(handleRequest(HttpMethod.Get, "/")) { assertEquals(HttpStatusCode.OK, response.status()) assertEquals("I am Groot!", response.content) } } }}

65/85

Page 66: Ktor - bkug.by

Тестированиеclass AppKtTest { @Test fun testIsIAmGroot() { withTestApplication(Application::myApp) { with(handleRequest(HttpMethod.Get, "/")) { assertEquals(HttpStatusCode.OK, response.status()) assertEquals("I am Groot!", response.content) } } }}

66/85

Page 67: Ktor - bkug.by

Autoreloadfun Application.myApp() { routing { get("/") { call.respondText("I am Groot!", ContentType.Text.Html) } }}

67/85

Page 68: Ktor - bkug.by

Autoreloadktor { deployment { port = 8080 watch = [ ktor-bkug ] }

application { modules = [ by.bkug.autoreload.AutoreloadKt.module ] }}

68/85

Page 69: Ktor - bkug.by

Autoreloadio.ktor.server.netty.DevelopmentEngine

70/85

Page 70: Ktor - bkug.by

AutoreloadApplicationEngineEnvironmentReloading

71/85

Page 71: Ktor - bkug.by

AutoreloadDemo?

72/85

Page 72: Ktor - bkug.by

СерверыNetty (ktor-server-netty),

Jetty (ktor-server-jetty),

Tomcat (ktor-server-tomcat)

Servlet 3.0+ (ktor-server-servlet)

73/85

Page 73: Ktor - bkug.by

HTTP Клиент

74/85

Page 74: Ktor - bkug.by

КлиентыApache HTTP (ktor-client-apache),

Jetty (ktor-client-jetty)

75/85

Page 75: Ktor - bkug.by

Пример

76/85

fun Application.myApp() { val client = HttpClient(Apache)}

Page 76: Ktor - bkug.by

Пример

77/85

fun Application.myApp() { val client = HttpClient(Apache)

routing { get("/call") { val text = client.get<String>( host = "localhost", port = 8081, path = "/text" )

call.respondText(text) } }}

Page 77: Ktor - bkug.by

Пример

78/85

fun Application.myApp() { val client = HttpClient(Apache)

routing { get("/call") { val text = client.get<String>( host = "localhost", port = 8081, path = "/text" )

call.respondText(text) } }}

Page 78: Ktor - bkug.by

Пример

79/85

val client = HttpClient(Apache) { install(JsonFeature)}

Page 79: Ktor - bkug.by

Пример

80/85

val user = client.get<User>( host = "localhost", port = 8081, path = "/json")

Page 80: Ktor - bkug.by

Пример

81/85

val user = client.get<User>( host = "localhost", port = 8081, path = "/json")

Page 81: Ktor - bkug.by

Про что еще можно было бы рассказать● Продвинутый роутинг● JSON● Статические данные (HTML, JS, …)● ExceptionHandling● IoC (DI)● HTTP/2● WebSockets● Как добавить $server? (Undertow, …)

82/85

Page 82: Ktor - bkug.by

Резюме● Ktor - connected systems● Application - Pipelines● Pipeline - Interceptors● Interceptors - Feature● Авторелоад● Тестирование● Клиент

83/85

Page 83: Ktor - bkug.by

Вопросы?

● Ktor.io: ktor.io● Awesome Kotlin: kotlin.link● Belarus Kotlin User Group: bkug.by

Page 84: Ktor - bkug.by

85/85