Ниже показан английский оригинал.
Headers
A NATS header is text: a name of printable ASCII without a colon and a one-line value; a header that does not fit fails the publish.
The framework's header map holds any bytes under any name, and NATS carries less.
A value that is not UTF-8, a value with a line break, and a name with a colon or a space have no wire form.
Such a header fails the publish with NatsError::Publish naming it, before anything reaches the server.
The message is not sent without it, because a header that silently disappears changes what the message means.
A message type declares its headers in its typed header contract, #[outgoing(headers = ..)], and a publish of that type that does not set them does not compile.
Headers count toward the server's payload limit together with the payload.
A delivery's headers are read with ctx.headers() or the Headers<T> extractor.
A Core delivery that is a request carries its inbox as the reply-to header, which a responder answers on.
The Nats- headers of JetStream are written by the policy and the publish steps.
Example
The tenant of an invoice travels as a header; a tenant name with a line break fails the publish, and the order is dropped.
- Macros
- Manual
/// The header contract of an invoice: the tenant it belongs to travels as `x-tenant`.
#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct Tenant {
#[serde(rename = "x-tenant")]
tenant: String,
}
#[derive(Debug, PartialEq, Deserialize, Serialize, Outgoing)]
#[outgoing(name = "invoices.issued", headers = Tenant)]
struct Invoice {
order_id: u64,
}
#[derive(OutSlot)]
#[publishes(Invoice)]
struct Invoices;
/// Issues the invoice of each order under its tenant. A tenant name with a line break has no
/// NATS form, so the publish fails and the order is dropped.
#[subscriber("orders.billed")]
async fn issue(order: &Order, Out(invoices): Out<impl Publisher, Invoices>) -> HandlerOutcome {
let tenant = Tenant {
tenant: order.tenant.clone(),
};
let sent = invoices
.message(&Invoice { order_id: order.id })
.with_headers(&tenant)
.publish()
.await;
match sent {
Ok(()) => HandlerOutcome::ack(),
Err(err) => {
tracing::error!(order.id, %err, "the invoice cannot carry its tenant");
HandlerOutcome::drop()
}
}
}
/// The header contract of an invoice: the tenant it belongs to travels as `x-tenant`.
#[derive(Debug, PartialEq, Deserialize, Serialize, JsonSchema)]
#[schemars(crate = "ruststream::schemars")]
struct Tenant {
#[serde(rename = "x-tenant")]
tenant: String,
}
#[derive(Debug, PartialEq, Deserialize, Serialize, Outgoing, JsonSchema)]
#[schemars(crate = "ruststream::schemars")]
#[outgoing(name = "invoices.issued", headers = Tenant)]
struct Invoice {
order_id: u64,
}
#[derive(OutSlot)]
#[publishes(Invoice)]
struct Invoices;
/// Issues the invoice of each order under its tenant. A tenant name with a line break has no
/// NATS form, so the publish fails and the order is dropped.
struct Issue;
impl<Entry> Handle<Order, (), Outs<(Entry,)>> for Issue
where
Entry: OutEntry<Invoices, Wire: Publisher>,
{
async fn handle(
&self,
order: &Order,
outs: &Outs<(Entry,)>,
_ctx: &mut Context<'_>,
) -> Result<(), HandlerOutcome> {
let tenant = Tenant {
tenant: order.tenant.clone(),
};
outs.get(Invoices)
.message(&Invoice { order_id: order.id })
.with_headers(&tenant)
.publish()
.await
.map_err(|err| {
tracing::error!(order.id, %err, "the invoice cannot carry its tenant");
HandlerOutcome::drop()
})
}
}