Read and write more than one format¶
This is for code that sits between an event and a transport, such as a messaging adapter. A service
that only ever uses binary mode over NATS or HTTP can call Encode and Decode on
cloudevents.Binary(...) directly, as in Send a CloudEvent over NATS.
Choose a writer¶
Binary mode is the one to prefer, because anything routing on an attribute can see it in a header. Pick the binding for the transport:
Use structured mode when the transport limits its headers. SQS carries ten message attributes, and an event with every optional attribute set and three extensions needs eleven in binary mode. SNS also refuses a body that is not UTF-8, which rules out a protobuf payload:
Publish¶
Encode returns an envelope.Wire. Put ContentType wherever the transport keeps its content type:
a native property on AMQP and RabbitMQ, the content-type header on NATS and SQS.
w, err := writer.Encode(e)
if err != nil {
return err
}
header := nats.Header(w.Headers)
if header == nil {
header = nats.Header{}
}
if w.ContentType != "" {
header["content-type"] = []string{w.ContentType}
}
err = client.PublishMsg(ctx, &nats.Msg{Subject: "orders.created", Header: header, Data: w.Body})
Read whatever arrives¶
Give Read every format the reader should accept. A reader that accepts both modes can be deployed
before any writer changes, which is what makes the change of mode safe:
readers := []envelope.Format{cloudevents.Binary(cloudevents.NATS), cloudevents.Structured()}
e, foreign, format, err := envelope.Read(
envelope.Wire{Headers: msg.Header, Body: msg.Data},
readers...,
)
if err != nil {
refused.Add(1)
logger.Error("refused a CloudEvent", "id", e.ID, "err", err)
return
}
Read returns three things beside the event:
foreignis everything on the wire that is not part of the event, such asNats-Msg-Id. Read it if you need it; do not send it on.formatis the one that decoded the message. Write a reply in it, so a structured-mode caller gets a structured-mode answer.errisenvelope.ErrUnrecognisedwhen no format claims the message andenvelope.ErrAmbiguouswhen two do. Neither decodes, soeis empty for both. Every other refusal carries the partial event, asDecode's do.
Do not order the formats to break a tie¶
It is tempting to treat the list as a priority order and take the first format that claims a
message. No order works: a structured-mode message carrying a stray ce-specversion header is
misread as malformed binary when binary goes first, and a binary event whose payload is itself a
structured CloudEvent is misread as the inner event when structured goes first. That is why Read
refuses a message two formats claim, rather than choosing.