Ktor拦截器对相同相位和覆盖的排序

时间:2020-03-18 23:33:50

标签: kotlin ktor

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

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)。在当前架构中这可能吗?我看不到任何方法可以覆盖合并并删除阶段“ Limit”的“最后”拦截器以外的所有拦截器。对于所有管道,另一个“限制”应保留在原处,而没有孩子的“限制”。其他拦截器(如Auth)应照常执行。

1 个答案:

答案 0 :(得分:0)

我将写给您第二部分的内容。我不确定它是否性感。基本上,当我使用自定义内容将新的孩子添加到路线时,我会计算路线中已有多少个具有相同选择器的父母。然后,我将这笔钱传递给拦截器,所以我知道当前孩子的嵌套位置是什么。

 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

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