Заказ перехватчика Ktor для той же фазы и приоритет

В настоящее время я пытаюсь реализовать следующий шаблон с функциями, конвейерами и перехватчиками:

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

В обеих функциях я использую это:

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

У меня две проблемы:

  1. При использовании PhaseAfter неправильный порядок. Первым должен быть вызван Auth, вторым - Limit. Почему лимит на первом месте? Как я могу предотвратить это? Примечание: это всегда должно зависеть от кодовой последовательности, первый блок должен выполняться первым. По неизвестной причине с PhaseBefore он работает должным образом. Но это кажется непоследовательным. Эти методы помещают Фазу в другие Фазы, но не выполняют Упорядочивание для слияния. Как это сделать?

insertPhaseAfter

[Фаза («Настройка»), Фаза («Мониторинг»), Фаза («Функции»), Фаза («Предел»), Фаза («Вызов»), Фаза («Откат»)]

[Фаза ('Настройка'), Фаза ('Мониторинг'), Фаза ('Функции'), Фаза ('Auth'), Фаза ('Вызов'), Фаза ('Откат')]

insertPhaseBefore

[Фаза ('Настройка'), Фаза ('Мониторинг'), Фаза ('Подтверждение'), Фаза ('Функции'), Фаза ('Вызов'), Фаза ('Откат')]

[Фаза («Настройка»), Фаза («Мониторинг»), Фаза («Предел»), Фаза («Характеристики»), Фаза («Вызов»), Фаза («Откат»)]

  1. Я хочу удалить Phase Interceptors.
rate(100) {
 route("/sub") {
   rate(5) {
     get("/critical") {
       call.respondText("Hello, World!")
     }
   }
   get("/profile/{id}") { TODO("...") }
 }
}

Таким образом, для / sub / critical следует вызывать только перехватчик со скоростью (5), а скорость (100) следует пропускать. Возможно ли это в нынешней архитектуре? Я не вижу способа отменить слияние и удалить все, кроме «последнего» перехватчика для «Предела» фазы. Другой «Предел» должен оставаться на месте для всех конвейеров без «Предела» в качестве дочернего элемента. Другие перехватчики (например, Auth) должны выполняться как обычно.


person peterulb    schedule 18.03.2020    source источник
comment
Вы когда-нибудь догадывались об этом? :) Я сейчас тоже зациклился на части 2   -  person Josttie    schedule 22.11.2020
comment
@Josttie Нет. Проблема с github для 1-го так и не была решена (просто перенесена на их внутренний трекер), и тогда я даже не стал беспокоиться о № 2. Imo ktor больше похож на доказательство концептуальной основы. Не стал бы использовать его ни для чего продуктивного. Но если вы когда-нибудь это узнаете, смело отвечайте. Удачи   -  person peterulb    schedule 22.11.2020


Ответы (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

Я использовал ту же структуру, что и там, только добавил подсчет и контекст. Надеюсь, это немного поможет!

person Josttie    schedule 23.11.2020