相同阶段的 Ktor 拦截器排序和覆盖
Ktor Interceptor Ordering for same Phase and overriding
我目前正在尝试使用功能、管道和拦截器实现以下模式:
auth {
limit(1) {
get {
call.respond("Cake1")
}
}
}
在这两个功能中,我使用这个:
pipeline.insertPhaseAfter(ApplicationCallPipeline.Features, myCustomPhase)
pipeline.intercept(appTokenAuthPhase)
我遇到以下两个问题:
- 使用PhaseAfter时,顺序错误。应该先调用 Auth,然后再调用 Limit。为什么 Limit 排在第一位?我怎样才能防止这种情况发生?注意:它应该始终取决于代码顺序,第一个块应该首先执行。由于某些未知原因,它与 PhaseBefore 一起按预期工作。但这似乎不一致。这些方法将阶段放置到其他阶段,但不对合并执行排序。这是怎么做到的?
insertPhaseAfter
[Phase('Setup'), Phase('Monitoring'), Phase('Features'), Phase('Limit'), Phase('Call'), Phase('Fallback')]
[Phase('Setup'), Phase('Monitoring'), Phase('Features'), Phase('Auth'), Phase('Call'), Phase('Fallback')]
insertPhaseBefore
[Phase('Setup'), Phase('Monitoring'), Phase('Auth'), Phase('Features'), Phase('Call'), Phase('Fallback')]
[Phase('Setup'), Phase('Monitoring'), Phase('Limit'), Phase('Features'), Phase('Call'), Phase('Fallback')]
- 我想删除相位拦截器。
rate(100) {
route("/sub") {
rate(5) {
get("/critical") {
call.respondText("Hello, World!")
}
}
get("/profile/{id}") { TODO("...") }
}
}
所以对于 /sub/critical 只应调用 Rate(5) 的拦截器,并应跳过 Rate(100)。这在当前架构中可能吗?我看不出有什么方法可以覆盖合并并删除除阶段 "Limit" 的 "last" 拦截器之外的所有内容。另一个 "Limit" 应该保留在没有 "Limit" 的所有管道中。其他拦截器(如 Auth)应照常执行。
我会把我为第 2 部分所做的写给你。我不确定它是否性感。基本上,当我用自定义的东西向路由添加新的 child 时,我会计算路由中已经有多少 parents 具有相同的选择器。然后我将那个数量传递给拦截器,所以我知道当前 child 的嵌套位置是什么。
fun Route.test(
message: String,
build: Route.() -> Unit,
): Route {
val testRoute = createChild(SimpleSelector("message"))
application.feature(SimpleInterceptor).apply {
interceptPipeline(testRoute, message, testRoute.countSimpleSelectors())
}
testRoute.build()
return testRoute
}
fun Route.countSimpleSelectors(): Int =
(parent?.countSimpleSelectors() ?: 0) +
if (parent?.selector is SimpleSelector) {
1
} else {
0
}
然后在拦截器中,我将添加与嵌套拦截器一样多的阶段。但是我是倒序添加的,也就是说最后添加的拦截器会运行先
fun interceptPipeline(
pipeline: ApplicationCallPipeline,
message: String,
nestingCounter: Int,
) {
pipeline.insertPhaseAfter(ApplicationCallPipeline.Features, Authentication.ChallengePhase)
val phases = (nestingCounter downTo 0).map {
simplePhase(it)
}
phases.fold(Authentication.ChallengePhase) { old, new ->
pipeline.insertPhaseAfter(old, new)
new
}
pipeline.intercept(phases.first()) {
val call = call
val simpleContext = SimpleContext.from(call)
TestPipeline().apply {
val subject = SimpleContext.from(call)
println("original subject: $message, new subject: ${subject.someMessage}")
subject.someMessage = message
}.execute(call, simpleContext)
}
}
现在链中最新添加的拦截器将 运行 首先。剩下的就是拦截器向管道添加上下文。此上下文可以是您想要的任何内容,因此您可以向上下文添加一个布尔值:isAlreadyHandled。第一个拦截器完成后可以翻转它,后面的拦截器可以忽略管道。
我基于:https://www.ximedes.com/2020-09-17/role-based-authorization-in-ktor/
和随附的 github 回购:https://github.com/ximedes/ktor-authorization
我使用了与那里相同的结构,只是添加了计数和上下文。希望对您有所帮助!
我目前正在尝试使用功能、管道和拦截器实现以下模式:
auth {
limit(1) {
get {
call.respond("Cake1")
}
}
}
在这两个功能中,我使用这个:
pipeline.insertPhaseAfter(ApplicationCallPipeline.Features, myCustomPhase)
pipeline.intercept(appTokenAuthPhase)
我遇到以下两个问题:
- 使用PhaseAfter时,顺序错误。应该先调用 Auth,然后再调用 Limit。为什么 Limit 排在第一位?我怎样才能防止这种情况发生?注意:它应该始终取决于代码顺序,第一个块应该首先执行。由于某些未知原因,它与 PhaseBefore 一起按预期工作。但这似乎不一致。这些方法将阶段放置到其他阶段,但不对合并执行排序。这是怎么做到的?
insertPhaseAfter
[Phase('Setup'), Phase('Monitoring'), Phase('Features'), Phase('Limit'), Phase('Call'), Phase('Fallback')]
[Phase('Setup'), Phase('Monitoring'), Phase('Features'), Phase('Auth'), Phase('Call'), Phase('Fallback')]
insertPhaseBefore
[Phase('Setup'), Phase('Monitoring'), Phase('Auth'), Phase('Features'), Phase('Call'), Phase('Fallback')]
[Phase('Setup'), Phase('Monitoring'), Phase('Limit'), Phase('Features'), Phase('Call'), Phase('Fallback')]
- 我想删除相位拦截器。
rate(100) {
route("/sub") {
rate(5) {
get("/critical") {
call.respondText("Hello, World!")
}
}
get("/profile/{id}") { TODO("...") }
}
}
所以对于 /sub/critical 只应调用 Rate(5) 的拦截器,并应跳过 Rate(100)。这在当前架构中可能吗?我看不出有什么方法可以覆盖合并并删除除阶段 "Limit" 的 "last" 拦截器之外的所有内容。另一个 "Limit" 应该保留在没有 "Limit" 的所有管道中。其他拦截器(如 Auth)应照常执行。
我会把我为第 2 部分所做的写给你。我不确定它是否性感。基本上,当我用自定义的东西向路由添加新的 child 时,我会计算路由中已经有多少 parents 具有相同的选择器。然后我将那个数量传递给拦截器,所以我知道当前 child 的嵌套位置是什么。
fun Route.test(
message: String,
build: Route.() -> Unit,
): Route {
val testRoute = createChild(SimpleSelector("message"))
application.feature(SimpleInterceptor).apply {
interceptPipeline(testRoute, message, testRoute.countSimpleSelectors())
}
testRoute.build()
return testRoute
}
fun Route.countSimpleSelectors(): Int =
(parent?.countSimpleSelectors() ?: 0) +
if (parent?.selector is SimpleSelector) {
1
} else {
0
}
然后在拦截器中,我将添加与嵌套拦截器一样多的阶段。但是我是倒序添加的,也就是说最后添加的拦截器会运行先
fun interceptPipeline(
pipeline: ApplicationCallPipeline,
message: String,
nestingCounter: Int,
) {
pipeline.insertPhaseAfter(ApplicationCallPipeline.Features, Authentication.ChallengePhase)
val phases = (nestingCounter downTo 0).map {
simplePhase(it)
}
phases.fold(Authentication.ChallengePhase) { old, new ->
pipeline.insertPhaseAfter(old, new)
new
}
pipeline.intercept(phases.first()) {
val call = call
val simpleContext = SimpleContext.from(call)
TestPipeline().apply {
val subject = SimpleContext.from(call)
println("original subject: $message, new subject: ${subject.someMessage}")
subject.someMessage = message
}.execute(call, simpleContext)
}
}
现在链中最新添加的拦截器将 运行 首先。剩下的就是拦截器向管道添加上下文。此上下文可以是您想要的任何内容,因此您可以向上下文添加一个布尔值:isAlreadyHandled。第一个拦截器完成后可以翻转它,后面的拦截器可以忽略管道。
我基于:https://www.ximedes.com/2020-09-17/role-based-authorization-in-ktor/
和随附的 github 回购:https://github.com/ximedes/ktor-authorization
我使用了与那里相同的结构,只是添加了计数和上下文。希望对您有所帮助!