【问题标题】:Ktor Interceptor Ordering for same Phase and overriding相同阶段的 Ktor 拦截器排序和覆盖
【发布时间】:2020-03-18 23:33:50
【问题描述】:

我目前正在尝试使用功能、管道和拦截器实现以下模式:

auth {
    limit(1) {
        get {
            call.respond("Cake1")
        }
    }
}

在这两个功能中,我都使用了这个:

pipeline.insertPhaseAfter(ApplicationCallPipeline.Features, myCustomPhase)
pipeline.intercept(appTokenAuthPhase)

我遇到以下两个问题:

  1. 使用 PhaseAfter 时,顺序错误。应该首先调用 Auth,其次应该调用 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')]

  1. 我想删除阶段拦截器。
rate(100) {
 route("/sub") {
   rate(5) {
     get("/critical") {
       call.respondText("Hello, World!")
     }
   }
   get("/profile/{id}") { TODO("...") }
 }
}

所以对于 /sub/critical 来说,只有带有 Rate(5) 的拦截器应该被调用,而 Rate(100) 应该被跳过。这在当前架构中可能吗?我认为没有办法覆盖合并并删除阶段“限制”的“最后一个”拦截器。对于所有没有“限制”的管道,另一个“限制”应该保持不变。其他拦截器(如 Auth)应照常执行。

【问题讨论】:

  • 你有没有想过这个问题? :) 我现在也坚持第 2 部分
  • @Josttie 不。 1st 的 github 问题从未得到解决(只是移到了他们的内部跟踪器),然后我什至没有理会 #2。 Imo ktor 似乎更像是一个概念证明框架。不会将它用于任何有成效的事情。但是,如果您发现它,请随时回答。祝你好运

标签: kotlin ktor


【解决方案1】:

我会写下我在第 2 部分所做的事情。我不确定它是否性感。基本上,当我用我的自定义东西向路线添加一个新孩子时,我会计算路线中有多少父母已经拥有相同的选择器。然后我将这个数量传递给拦截器,这样我就知道当前孩子的嵌套位置是什么。

 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

我使用了与那里相同的结构,只是添加了计数和上下文。我希望这会有所帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多