Realtime
WebLogic pushes events to browsers over server-sent events (/_weblogic/realtime/sse) or
WebSocket (/_weblogic/realtime/ws), with its own hub, written on the shared framework.
Publishing
public sealed class OrderNotifier(WebRealtimeHub hub)
{
public Task OrderShippedAsync(Order order, CancellationToken ct) =>
hub.PublishAsync("order.shipped", new { order.Id, order.Number }, WebRealtimeAudience.User(order.CustomerId), cancellationToken: ct);
}
| Audience | Who receives it |
|---|---|
Public |
Everyone connected (anonymous included, if realtime.allowAnonymous) |
Authenticated |
Signed-in connections |
Group(name), Permission(name) |
Members of a group / holders of a permission |
User(id) |
Every connection of one user |
Tenant(id) |
Connections of one tenant |
Internal |
Holders of realtime.internalPermission (operator dashboards) |
In multi-tenant apps use PublishToTenantAsync(tenantId, ...): the event only reaches connections of
that tenant, whatever the audience. Envelopes carry a type, an optional channel, data, an id and a
timestamp; clients can subscribe to channels.
Bridging CodeLogic events
web.BridgeEvent<UserRegisteredEvent>("site.member-joined",
_ => WebRealtimeAudience.Public,
_ => new { message = "A new member just joined." });
Contributors and plugins can bridge too (b.BridgeEvent<T>(...)); the bridge is removed with them.
Choose the data deliberately: only what the audience may see.
The browser side
document.addEventListener('wl:ready', ({ detail: wl }) => {
wl.realtime.connect({ transport: 'sse', channels: ['orders'] });
wl.realtime.subscribe('order.shipped', e => wl.ui.toast(`Order ${e.data.number} shipped`, 'success'));
});
With realtime.clientAutoConnect the client connects on page load. It reconnects with back-off and
resumes from Last-Event-ID; the replay buffer only replays events the connection's audience may see.
Security
- Connections are re-grouped on sign-in, sign-out and permission changes; signing out closes the user's connections.
- A connection opened by a signed-in session ends with that session: a revoked device, an evicted or
expired session closes it at once on the same node (
SessionEndedEvent) and withinrealtime.sessionCheckSeconds(30) on every other node, which look the session up in the shared session store. Tickets carry the session too. - WebSocket upgrades check
Originagainst the site andrealtime.allowedOrigins. - Cross-origin clients get a short-lived ticket from
/_weblogic/realtime/ticket(60 s by default,realtime.ticketSeconds). A ticket can be used more than once inside that window, so keep it short and do not log it. - Limits: total connections, connections per client, per-connection queue (slow consumers are dropped), maximum connection time.
realtime.broadcastRequests(off by default) streams request summaries to theInternalaudience, without client IPs.
Receiving messages
WebSocket clients can send messages. Handle them with an IWebRealtimeReceiver
(web.UseRealtimeReceiver<T>()); treat every message as untrusted input and check
connection.Identity before acting.
Several nodes
The hub delivers to connections on its own node. Supply an IWebRealtimeBackplane
(web.UseRealtimeBackplane<T>(): PublishAsync + Subscribe) on Redis, a database queue or your
message bus, and every node delivers every event. See the cluster scenario.