lilydocs
HTTP APIWebSocketConsumer
Pipeline3 dk okuma

Guard ve yetkilendirme

Handler çalışmadan önce delivery kapsamındaki servislerle işleme izni verin veya açık bir hata sınıfıyla reddedin.

Guard, kabul edilmiş bir mesajın handler’a ulaşmasına izin verir. Bütün middleware girişleri tamamlandıktan sonra, handler owner’ı ve typed extractor’lar çözülmeden önce çalışır. Sonuç bool değil, Result<(), QueueHandlerError> biçimindedir.

Scoped policy ile bir guard#

Aşağıdaki tam uygulama audit verisini middleware’de hazırlar, OrderPolicy servisini guard’da çözer ve aynı Scoped instance’ı handler’a taşır. Örnekteki sürüm kontrolü küçük bir uygulama politikasıdır; credential doğrulaması değildir. Gerçek yetkilendirmede doğrulanmış kimlik ve domain yetkilerini kendi servisiniz değerlendirir.

guards.rs
use std::sync::Arc;
use lilyrs::consumer::{Consumer, ConsumerError, Extensions, Injectable, ServiceTrait};
use lilyrs::queue::{
    DeliveryContext, Json, Local, QueueDeliveryExchange, QueueGuard, QueueHandlerError,
    QueueMiddleware, QueuePipelineComponentInitError, Service, async_trait, guard,
    queue, queue_service,
};
use serde::Deserialize;

#[derive(Clone)]
struct DeliveryAudit { event_id: String }

struct Audit;
#[async_trait]
impl QueueMiddleware for Audit {
    async fn new(_: Arc<Extensions>) -> Result<Self, QueuePipelineComponentInitError> {
        Ok(Self)
    }
    async fn before_delivery(&self, exchange: &mut QueueDeliveryExchange<'_>)
        -> Result<(), QueueHandlerError>
    {
        let event_id = exchange.context().event_id().into_inner().to_string();
        exchange.insert_local(DeliveryAudit { event_id })?;
        Ok(())
    }
}

#[derive(Injectable)]
#[service(lifetime = "Scoped")]
struct OrderPolicy;
impl ServiceTrait for OrderPolicy {}
impl OrderPolicy {
    fn accepts(&self, context: &DeliveryContext) -> bool {
        context.schema_version().into_inner() == 1
    }
}

struct OrderGuard;
#[async_trait]
impl QueueGuard for OrderGuard {
    async fn new(_: Arc<Extensions>) -> Result<Self, QueuePipelineComponentInitError> {
        Ok(Self)
    }
    async fn can_activate(&self, exchange: &mut QueueDeliveryExchange<'_>)
        -> Result<(), QueueHandlerError>
    {
        let policy = exchange.service::<OrderPolicy>().await
            .map_err(|error| QueueHandlerError::retryable_with_source("POLICY_UNAVAILABLE", error))?;
        if policy.accepts(exchange.context()) { Ok(()) }
        else { Err(QueueHandlerError::permanent("ORDER_NOT_ADMITTED")) }
    }
}

#[derive(Deserialize)]
struct OrderCreated { order_id: String }
#[derive(Injectable)]
#[service(lifetime = "Singleton")]
struct OrderWorker;
impl ServiceTrait for OrderWorker {}

#[queue_service]
#[guard(OrderGuard)]
impl OrderWorker {
    #[queue("orders.created", version = 1, content = "json")]
    async fn created(&self, Service(_policy): Service<OrderPolicy>,
        Local(audit): Local<DeliveryAudit>, Json(event): Json<OrderCreated>)
        -> Result<(), QueueHandlerError>
    {
        if event.order_id.is_empty() {
            return Err(QueueHandlerError::permanent("ORDER_ID_REQUIRED"));
        }
        println!("event={}, order={}", audit.event_id, event.order_id);
        Ok(())
    }
}

#[tokio::main]
async fn main() -> Result<(), ConsumerError> {
    Consumer::builder().middleware::<Audit>().run().await
}

Örneği indirin. İlk uygulamanın orders.created configuration’ını kullanın. Manifestte lilyrs = { version = "0.1.0", features = ["consumer"] }, serde için derive, tokio için macros ve rt-multi-thread yeterlidir.

Tek bir kayıt düzeyi seçin#

Kayıt Kapsam
.guard::<OrderGuard>() Bütün seçilmiş handler planları
Impl üzerinde #[guard(OrderGuard)] O queue service’in handler’ları
Handler üzerinde #[guard(OrderGuard)] Yalnız o handler

Global → service → handler sırası korunur. Yukarıdaki guard service üzerinde kayıtlıdır; ayrıca builder’a eklemeyin. Aynı effective planda concrete tip tekrarı, aynı tipin hem guard hem middleware olması veya 64’ten fazla guard startup hatasıdır. Her marker tek tip alır; birden çok guard için marker tekrarlanır.

Kimlik, hata ve rate limiting#

RabbitMQ kullanıcı/parola veya mTLS bağlantısı mesajdaki user_id alanını doğrulanmış kimliğe dönüştürmez. Uygulama middleware’i credential veya imzayı doğrulayıp Local<VerifiedActor> üretebilir; guard bu sonucu kullanır. Hazır Consumer JWT/OIDC doğrulayıcısı yoktur.

Kalıcı ret için permanent, geçici bağımlılık sorunu için gerekçeli retryable seçin. Redis destekli limit kontrolü de bir uygulama guard’ı olabilir; retryable limit reddi normal retry hakkını tüketir ve kalıcı yoğunlukta DLQ’ya ulaşabilir. HTTP 403/429 cevabı veya otomatik bekleme kuyruğu oluşturulmaz. Hata modeli bu kararın devamını açıklar.

Ömür ve cancellation#

Constructor bir concrete tip için Consumer başlangıcında bir kez çalışır. Scoped veya Transient servisi burada tutmayın; exchange.service::<T>().await ile aktif delivery’den çözün. Cancellation ve deadline exchange içindedir; callback’e ayrı WebSocket cancellation parametresi eklenmez. Dış çağrıları ortak delivery bütçesine bağlayın.

Bu sayfanın kaynakları v0.1.0

Anlatım, incelenen framework commit’ine dayanır.

HIZLI BAŞLANGIÇ
Dokümantasyon
Kaynak kod