Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Azure Web PubSub routes system and user events to an upstream server so that you can authorize clients and handle events from the clients. This article explains how the service calls upstream handlers, then shows complete implementations in C#, JavaScript, Java, and Python, followed by debugging tips.
Prerequisites
- An Azure Web PubSub resource with a hub (the examples use
Sample_ChatApp). - Azure CLI 2.22.0 or later.
- Azure Web PubSub tunnel tool (
npm install -g @azure/web-pubsub-tunnel-tool).
Understand how Azure Web PubSub calls upstreams
Azure Web PubSub forwards events to the HTTP endpoint you configure per hub. Each callback arrives as a CloudEvent that contains metadata such as the event type (ce-type), the user identity (ce-userid), and the request body. The service expects:
- A reachable endpoint defined in the hub's event handler settings (for example,
tunnel:///eventhandlerwhen using the tunnel tool, orhttps://contoso.com/eventhandlerwhen your upstream server endpoint is exposed and reachable). connectresponses that returnuserId, initial groups to join, and other negotiated properties as described in the connect event spec.- Standard HTTP status codes. Returning
401or403fromconnectrejects the client, while returning2xxfrom user events acknowledges the message.
Keep this contract in mind while implementing and testing your upstream logic.
Implement the upstream server
The upstream server processes events that the service forwards to any reachable HTTP endpoint. You can expose your handler publicly (for example, https://contoso.com/eventhandler) or keep it private behind the Web PubSub local tunnel tool while developing locally. The examples below use the tunnel endpoint for convenience, but the handler logic works the same no matter how it's hosted.
Configure the hub to call your upstream
Important
Replace <your-unique-resource-name> with the name of your Azure Web PubSub resource and keep the hub name that matches your app. When deploying to a publicly reachable server, set url-template to the fully qualified URL (for example, https://contoso.com/eventhandler). When testing locally through the tunnel, use tunnel:///eventhandler as shown below.
az webpubsub hub create \
-n "<your-unique-resource-name>" \
-g "myResourceGroup" \
--hub-name "Sample_ChatApp" \
--event-handler url-template="tunnel:///eventhandler" user-event-pattern="*" system-event="connect" system-event="connected" system-event="disconnected"
Sample upstream implementations
The upstream server handles events based on your event handler configuration. The examples below demonstrate handling common events:
connectevents to validate clients and assign auserId.connectedevents to log successful joins.disconnectedevents to capture why clients leave and clean up the resources.- User events (for example,
message) that carry user payloads so your upstream can run your application logic.
You can configure which system events and user event patterns the service forwards to your upstream by adjusting the --system-event and --user-event-pattern parameters in your hub settings.
Each language sample below expects the upstream to listen on port 8080 at /eventhandler.
Note
The tunnel tool embeds a userId inside each client’s SAS token. Azure Web PubSub passes this value to your upstream through the ce-userId CloudEvent header (surfaced as ConnectionContext.UserId in the .NET and JavaScript helpers). The samples below read that header whenever it exists and only generate random IDs for demonstration. Replace that fallback with your production authentication logic.
Prerequisites
- .NET 8 SDK installed.
Initialize the project
dotnet new web -n SampleChatApp
cd SampleChatApp
Install packages
dotnet add package Microsoft.Azure.WebPubSub.AspNetCore
Add the code
Replace the contents of Program.cs with the following single-file sample. It builds and runs the upstream handler on port 8080.
using System;
using Azure.Core;
using Azure.Messaging.WebPubSub;
using Microsoft.Azure.WebPubSub.AspNetCore;
using Microsoft.Azure.WebPubSub.Common;
var connectionString = Environment.GetEnvironmentVariable("WebPubSubConnectionString")
?? throw new InvalidOperationException("Set the WebPubSubConnectionString environment variable.");
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddWebPubSub(o => o.ServiceEndpoint = new WebPubSubServiceEndpoint(connectionString))
.AddWebPubSubServiceClient<Sample_ChatApp>();
var app = builder.Build();
app.MapWebPubSubHub<Sample_ChatApp>("/eventhandler/{*path}");
app.Run("http://localhost:8080");
sealed class Sample_ChatApp : WebPubSubHub
{
private readonly WebPubSubServiceClient<Sample_ChatApp> _serviceClient;
public Sample_ChatApp(WebPubSubServiceClient<Sample_ChatApp> serviceClient)
{
_serviceClient = serviceClient;
}
public override ValueTask<ConnectEventResponse> OnConnectAsync(
ConnectEventRequest request,
CancellationToken cancellationToken)
{
var userId = request.ConnectionContext.UserId;
if (string.IsNullOrEmpty(userId))
{
// Demo only: assign a random ID when the tunnel doesn't include one in the SAS token.
// Production apps should authenticate callers and issue their own stable user IDs.
userId = Guid.NewGuid().ToString();
Console.WriteLine($"[CONNECT] Generated demo user id: {userId}");
}
else
{
Console.WriteLine($"[CONNECT] Received user id: {userId}");
}
return new ValueTask<ConnectEventResponse>(
request.CreateResponse(userId: userId, null, null, null));
}
public override Task OnConnectedAsync(ConnectedEventRequest request)
{
Console.WriteLine($"[SYSTEM] {request.ConnectionContext.UserId} joined.");
return Task.CompletedTask;
}
public override Task OnDisconnectedAsync(DisconnectedEventRequest request)
{
Console.WriteLine($"[SYSTEM] {request.ConnectionContext.UserId} disconnected: {request.Reason}");
return Task.CompletedTask;
}
public override async ValueTask<UserEventResponse> OnMessageReceivedAsync(
UserEventRequest request,
CancellationToken cancellationToken)
{
await _serviceClient.SendToAllAsync(
RequestContent.Create(new
{
from = request.ConnectionContext.UserId,
message = request.Data.ToString()
}),
ContentType.ApplicationJson);
return new UserEventResponse();
}
}
Run the upstream
set WebPubSubConnectionString=<your connection string> # use export on macOS/Linux
dotnet run
Debug event handlers
The tunnel lets you keep a local server private while still receiving callbacks from Azure Web PubSub. Start it alongside your upstream to inspect CloudEvents end to end. It also provides a way to establish a client connection to Web PubSub easily so you can try the end-to-end flow in the tunnel web page.
Tip
See Azure Web PubSub local tunnel tool for installation, authentication, and troubleshooting guidance.
export WebPubSubConnectionString="<your connection string>"
awps-tunnel run --hub Sample_ChatApp --upstream http://localhost:8080 --verbose
- Open the web view URL that the tunnel prints (for example, http://127.0.0.1:9080). The Server tab shows requests flowing to your upstream, while the Client tab lets you establish WebSocket connections.
- Use the Client tab’s Connect button (or copy the connection URL it displays) to spin up client connections.
Inspect CloudEvents and service telemetry
- Log
ce-type,ce-userid, and query parameters inside your upstream code to see which phase (connect,connected,disconnected, user event) triggered the callback. - Return descriptive HTTP status codes (for example,
401for missing auth or400for malformed input). The tunnel UI and service diagnostics surface these failures immediately. - Use Azure Web PubSub metrics and Live Trace in the portal to confirm whether events reach the upstream when debugging in production or other remote environments.