lilydocs
HTTP APIWebSocketConsumer
Mesajlar3 dk okuma

Typed extractor’lar

Mesaj metadata’sını, servisleri ve tek payload’ı owned action parametreleri olarak alın.

Extractor'lar action'ın ihtiyacını imzada görünür kılar. Framework route'u bulduktan sonra bütün parts extractor'larını, ardından tek payload consumer'ı çalıştırır. Action iş kuralına odaklanır.

Doğru extractor’ı seçin#

İhtiyaç Extractor
Bağlantı hedefleri ve kimlik görünümü WebSocketContext, Arc<WebSocketContext>
Mesaj ID, deadline ve metadata WebSocketMessageContext
Route bilgisi ConnectionId, Namespace, EventName
Envelope metadata MessageHeaders
Doğrulanmış kimlik Principal, Option<Principal>
Typed local veri ConnectionLocal<T>, MessageLocal<T>
Aktif scope'taki servis Service<T>, Service<dyn Trait>
JSON DTO Payload<T>
Typed text veya binary TextPayload, BinaryPayload
Decode edilmiş payload enum'u RawPayload
Orijinal application frame'i RawEnvelope
İşlem kontrolü ExecutionCancellation, MessageDeadline

RawPayload tam wire byte'ı değildir. RawEnvelope, payload decode'u tetiklemeden tek tüketim hakkını alır. Aynı imzada bunlardan ikisini kullanmak iki ayrı bakış almak anlamına gelmez; iki payload consumer olarak reddedilir.

Tam örnek#

Örnek özel bir parts extractor, JSON kontrolü, binary ACK, Close ve bounded action içerir. İlk uygulamanın bağımlılıklarını kullanır. AuthorizedSubject yalnızca önceden doğrulanmış Principal varlığını kontrol eder; identity middleware kurmadan validated action'ı kabul edilmez.

extractors.rs
use std::{future::Future, sync::Arc};
use lilyrs::websocket::{Ack, BinaryPayload, CloseConnection, ExecutionCancellation,
    Extensions, FromWebSocketMessageParts, MessageDeadline, NoReply, Payload,
    WebSocketActionError, WebSocketController, WebSocketControllerInitError,
    WebSocketControllerTrait, WebSocketErrorCode, WebSocketMessageInvocation,
    async_trait, websocket_controller};
use serde::Deserialize;

pub struct AuthorizedSubject(String);
impl FromWebSocketMessageParts for AuthorizedSubject {
    type Rejection = WebSocketActionError;
    fn from_message_parts(invocation: &mut WebSocketMessageInvocation)
        -> impl Future<Output = Result<Self, Self::Rejection>> + Send
    {
        let result = invocation.principal().map(|p| Self(p.subject().to_owned()))
            .ok_or_else(|| WebSocketActionError::rejected(
                WebSocketErrorCode::new("AUTH_REQUIRED").expect("sabit kod"),
                "Kimlik doğrulaması gerekli.",
            ).expect("sabit mesaj"));
        std::future::ready(result)
    }
}

#[derive(Deserialize)]
struct TextInput { text: String }
#[derive(WebSocketController)]
#[namespace("techniques")]
#[timeout(seconds = 10)]
#[asyncapi(tag = "techniques", summary = "Mesaj teknikleri")]
struct TechniquesController;
#[async_trait]
impl WebSocketControllerTrait for TechniquesController {
    async fn new(_: Arc<Extensions>) -> Result<Self, WebSocketControllerInitError> { Ok(Self) }
}
#[websocket_controller]
impl TechniquesController {
    #[message("validated")]
    #[asyncapi(summary = "Boş olmayan metin", operation_id = "validatedText")]
    async fn validated(&self, Payload(input): Payload<TextInput>, subject: AuthorizedSubject)
        -> Result<Ack<String>, WebSocketActionError>
    {
        if input.text.trim().is_empty() {
            return Err(WebSocketActionError::rejected(
                WebSocketErrorCode::new("TEXT_REQUIRED").expect("sabit kod"),
                "Metin boş olamaz.",
            ).expect("sabit mesaj"));
        }
        let _ = subject;
        Ok(Ack::new(input.text))
    }
    #[message("binary")]
    async fn binary(&self, BinaryPayload(bytes): BinaryPayload)
        -> Result<Ack<Vec<u8>>, WebSocketActionError>
    { Ok(Ack::binary(bytes)) }
    #[message("stop")]
    async fn stop(&self) -> Result<CloseConnection, WebSocketActionError> {
        CloseConnection::normal("completed").map_err(WebSocketActionError::internal)
    }
    #[message("bounded")]
    #[timeout(seconds = 2)]
    async fn bounded(&self, stopping: ExecutionCancellation, deadline: MessageDeadline)
        -> Result<NoReply, WebSocketActionError>
    {
        let work = tokio::time::sleep(std::time::Duration::from_millis(10));
        let _remaining = deadline.remaining();
        tokio::select! {
            biased;
            _ = stopping.cancelled() => Ok(NoReply),
            _ = work => Ok(NoReply),
        }
    }
}

Tam örnek dosyasını indirin.

İmza kuralları#

En fazla 16 owned extractor kullanılabilir. Referans parametreler, raw WsRequest veya doğrudan invocation parametresi action imzasında kabul edilmez. Payload<T>, Service<S> sıralaması geçerlidir; Service parts olduğu için önce çözülür. Bir action'da yalnızca bir payload consumer bulunur.

Örnekteki özel FromWebSocketMessageParts implementation'ı public genişletme noktasıdır. Invocation bu implementation'ın girişidir; action parametresi değildir. DeserializeOwned gerektiren DTO'yu Serde ile tanımlayın.

Optional değer ve hata#

Optional destek bütün tiplere otomatik uygulanmaz. Option<Principal>, Option<ConnectionLocal<T>>, Option<MessageLocal<T>> yokluğu temsil edebilir. Option<Payload<T>> veya Option<Service<T>> için aynı davranışı varsaymayın. Bozuk veri None diye yutulmaz.

Serde parse ile domain validation ayrıdır. Örnekte text alanının string olması parse, boş olmaması iş kuralıdır. Hatalar ve doğrulama sayfası bu ayrımı public error'a dönüştürür. Lifecycle hook'larında payload yoktur ve cancellation fazı hook sözleşmesine uymalıdır.

Bu sayfanın kaynakları v0.1.0

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

HIZLI BAŞLANGIÇ
Dokümantasyon
Kaynak kod