Server Applications

预计阅读时间:5分钟

Ktor server introduction and key concepts

目录:

Application and ApplicationEnvironment

ktor应用程序的运行实例由Application类表示. ktor 应用程序由一组模块 (可能是一个模块 )组成. 每个模块都是一个普通的kotlin lambda或一个函数(通常具有一个应用程序实例作为接收器或参数).

在具有应用程序配置ApplicationEnvironment表示的环境内部启动应用 程序 (有关更多详细信息,请参见" 配置"页面).

ktor 服务器从环境启动,并控制应用程序生命周期:应用程序实例是由环境创建和销毁的(取决于实现方式,它可以延迟创建它或提供热重载功能). 因此,停止应用程序并不总是意味着服务器正在停止:例如,可以在服务器继续运行时重新加载应用程序.

启动应用程序时,应用程序模块将一个一个地启动,并且每个模块都可以配置该应用程序的实例. 通过安装功能部件和拦截管道来配置应用程序实例.

有关更多详细信息,请参见生命周期 .

Features

功能是可以插入到应用程序中的特定功能的一部分. 它通常拦截请求和响应并执行其特定功能. 例如, 默认标头功能拦截响应并追加DateServer标头. 可以使用以下install功能install功能安装到应用程序中:

application.install(DefaultHeaders) {
   // configure feature
}

对于某些功能,配置lambda可能是可选的. 一个功能只能安装一次; 但是,在某些情况下需要配置组成. 对于此类功能,有些帮助程序功能会安装尚未安装的功能并应用配置lambda. 例如, routing {} .

Calls and pipelines

在ktor中,一对传入的请求和响应(完成或未完成)被称为应用程序调用ApplicationCall ). 每个应用程序调用都会通过由几个(或没有) 拦截器组成的应用程序调用管道ApplicationCallPipeline )传递. 拦截器由一个被调用的一个和每一个拦截器可以修改通过PROCEEDING请求或响应和控制流水线执行( proceed()到下一拦截或精加工( finish()finishAll()的整个流水线执行(从而下一个拦截器是未调用,请参见PipelineContext了解详细信息). 它还可以装饰其余的拦截器链,在proceed()调用之前和之后proceed()其他操作.

考虑以下装饰示例:

intercept {
    myPrepare()
    try {
        proceed()
    } finally {
        myComplete()
    }
}

管道可能包含多个阶段 . 每个拦截器都在特定阶段进行注册. 因此,拦截器按其阶段顺序执行. 有关更详细的说明,请参见管道文档.

Application call

An application call consists of a pair of request with response and a set of parameters. So an application call pipeline has a pair of receive and send pipelines. The request’s content (body) could be received using ApplicationCall.receive<T>() where T is an expected type of content. For example, call.receive<String>() reads requests body as a String. Some types could be received with no additional configuration out of the box while receiving a custom type may require a feature installation or configuration. Every receive() causes receive pipeline (ApplicationCallPipeline.receivePipeline) to be executed from the beginning so every receive pipeline interceptor could transform or by-pass request body. The original body object type is ByteReadChannel (asynchronous byte channel).

可以通过执行响应管道的ApplicationCall.respond(Any)函数调用( ApplicationCallPipeline.respondPipeline )来提供应用程序响应主体. 与接收管道类似,每个响应管道拦截器都可以转换响应对象. 最后,应将响应对象转换为OutgoingContent的实例.

一组扩展函数respondTextrespondBytesreceiveTextreceiveParameters等简化请求和响应对象的结构.

Routing

空的应用程序没有拦截器,因此将为每个请求生成404 Not Found. 应拦截应用程序调用管道以处理请求. 这样的拦截器可以根据请求URI进行响应,如下所示:

intercept {
    val uri = call.request.uri
    when {
        uri == "/" -> call.respondText("Hello, World!")
        uri.startsWith("/profile/") -> { TODO("...") }
    }
}

当然,这种方法有很多缺点. 幸运的是,存在用于结构化请求处理的路由功能,该功能确实拦截了应用程序调用管道,并提供了一种注册路由处理程序的方法. 由于路由唯一要做的就是拦截应用程序调用管道,因此使用路由进行手动拦截也可以. 路由由具有处理程序和拦截器的路由树组成. ktor中的一组扩展函数提供了一种注册处理程序的简便方法,如下所示:

routing {
    get("/") {
        call.respondText("Hello, World!")
    }
    get("/profile/{id}") { TODO("...") }
}

请注意,路由被组织成一棵树,因此您可以声明结构化路由:

routing {
    route("profile/{id}") {
        get("view") { TODO("...") }
        get("settings") { TODO("...") }
    }
}

路由路径可以包含常量部分和参数,例如上例中的{id} . 使用属性call.parameters可以访问捕获的设置值.

Content Negotiation

ContentNegotiation功能提供了一种协商哑剧类型并使用AcceptContent-Type标头转换类型的方法. 可以为特定的内容类型注册内容转换器,以接收和响应对象. 开箱即用的有JacksonGsonkotlinx.serialization内容转换器,可以将其插入该功能.

Example:

install(ContentNegotiation) {
    gson {
        // Configure Gson here
    }
}
routing {
    get("/") {
        call.respond(MyData("Hello, World!"))
    }
}

What’s next

by  ICOPY.SITE