Ktor 是一个使用强大的 Kotlin 语言在互联系统中构建异步服务器与客户端的框架。
Ktor 有十分好用的语法糖,可以快速的声明一个接口。
但是,Ktor 默认对 put 和delete的请求支持 不是很好,主要是Ktor默认只能解析put和delete请求的 url 参数,无法像post请求一样获取body里的参数,但是没关系,通过对源码的观察,我们可以照猫画虎。
先观察,为什么 post请求可以获取body里的参数。
/**
* Builds a route to match `POST` requests with specified [path] receiving request body content of type [R]
*/
@ContextDsl
@JvmName("postTyped")
inline fun <reified R : Any> Route.post(
path: String,
crossinline body: suspend PipelineContext<Unit, ApplicationCall>.(R) -> Unit
): Route {
return route(path, HttpMethod.Post) {
handle {
body(call.receive())
}
}
}
核心在于多了这个方法,多了这个带泛型的post方法。
让我们对比下,只能获取url参数的get 方法
typealias PipelineInterceptor<TSubject, TContext> = suspend PipelineContext<TSubject, TContext>.(TSubject) -> Unit
/**
* Builds a route to match `GET` requests with specified [path]
*/
@ContextDsl
fun Route.get(path: String, body: PipelineInterceptor<Unit, ApplicationCall>): Route {
return route(path, HttpMethod.Get) { handle(body) }
}
对比之下,我们可以看到,最大的区别在于 handle(body) 和
handle {
body(call.receive())
}
这里,查阅源码,可知,call.receive() 会读取request 中body中的内容,此时我们的处理程序就可以 获取到body 中的内容了。需要注意的是,一个request, call.receive()只能执行一次,无法重复执行。
最后补充代码如下
@ContextDsl
@JvmName("putTyped")
inline fun <reified T : Any> Route.put(
path: String,
crossinline body: suspend PipelineContext<Unit, ApplicationCall>.(T) -> Unit
): Route {
return route(path, HttpMethod.Put) {
handle {
body(call.receive())
}
}
}
@ContextDsl
@JvmName("deleteTyped")
inline fun <reified T : Any> Route.delete(
path: String,
crossinline body: suspend PipelineContext<Unit, ApplicationCall>.(T) -> Unit
): Route {
return route(path, HttpMethod.Delete) {
handle {
body(call.receive())
}
}
}
需要注意两个 注解
@ContextDsl 所注解的方法,在代码使用的地方,会被特殊高亮。
@JvmName() 这是 用于在 编译为java 代码的时候,在java 代码中区分 同方法名,同参数名,但是泛型类型 不同的时候,用于指定方法名的 注解。