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.
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),
}
}
}İ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.