Quick start: publish and subscribe messages in Azure Functions

In this tutorial you’ll learn how to publish messages and subscribe them using Azure Web PubSub with Azure Functions.

The complete code sample of this tutorial can be found here.

Prerequisites

  1. .NET Core 3.1 or above
  2. Azure Function Core Tools(v3)
  3. Create an Azure Web PubSub resource

Setup publisher

  1. New a timer trigger. After run below command, select dotnet -> Timer Trigger -> notifications following prompt messages.

    func new
    
  2. Remove the extensionBundle settings in host.json to add explicit version of extensions in next step. So the file would be like below.

    {
        "version": "2.0",
        "logging": {
            "applicationInsights": {
                "samplingSettings": {
                    "isEnabled": true,
                    "excludedTypes": "Request"
                }
            }
        }
    }
    
  3. Install Azure Web PubSub function extensions

    func extensions install --package Microsoft.Azure.WebJobs.Extensions.WebPubSub --version 1.0.0-beta.1
    
  4. Update notifications.cs to below

    using System;
    using System.Threading.Tasks;
    using Microsoft.Azure.WebJobs;
    using Microsoft.Azure.WebJobs.Extensions.WebPubSub;
    using Microsoft.Extensions.Logging;
    
    public static class notification
    {
        [FunctionName("notification")]
        public static async Task Run([TimerTrigger("*/10 * * * * *")]TimerInfo myTimer, ILogger log,
            [WebPubSub(Hub = "notification")] IAsyncCollector<WebPubSubOperation> operations)
        {
            await operations.AddAsync(new SendToAll
            {
                Message = BinaryData.FromString($"[DateTime: {DateTime.Now}] Temperature: {GetValue(23, 1)}{'\xB0'}C, Humidity: {GetValue(40, 2)}%"),
                DataType = MessageDataType.Text
            });
        }
    
        private static string GetValue(double baseNum, double floatNum)
        {
            var rng = new Random();
            var value = baseNum + floatNum * 2 * (rng.NextDouble() - 0.5);
            return value.ToString("0.000");
        }
    }
    
  5. New a Http Trigger function to help generate service access url for clients. After run below command, select and enter Http Trigger -> login.

    func new
    
  6. Update login.cs as below:

    using Microsoft.AspNetCore.Http;
    using Microsoft.Azure.WebJobs;
    using Microsoft.Azure.WebJobs.Extensions.Http;
    using Microsoft.Azure.WebJobs.Extensions.WebPubSub;
    using Microsoft.Extensions.Logging;
    
    [FunctionName("login")]
    public static WebPubSubConnection Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
        [WebPubSubConnection(Hub = "notification")] WebPubSubConnection connection,
        ILogger log)
    {
        log.LogInformation("Connecting...");
        return connection;
    }
    
  7. Update local.settings.json to use service connection string get from Azure Portal -> Keys. And set CORS to allow all for local testing purpose.

    {
        "IsEncrypted": false,
        "Values": {
            "AzureWebJobsStorage": "UseDevelopmentStorage=true",
            "FUNCTIONS_WORKER_RUNTIME": "dotnet",
            "WebPubSubConnectionString": "<connection-string>"
        },
        "Host": {
            "LocalHttpPort": 7071,
            "CORS": "*"
        }
    }
    
  8. Run the function.

    func start
    

Setup subscriber

In Azure Web PubSub you can connect to the service and subscribe to messages through WebSocket connections. WebSocket is a full-duplex communication channel so service can push messages to your client in real time. You can use any API/library that supports WebSocket to do so. Here is an example in csharp:

  1. First install required dependencies:

    dotnet add package Newtonsoft.Json --version 13.0.1
    
  2. Then use WebSocket API to connect to service.

    static async Task Main(string[] args)
    {
        var response = await _httpClient.GetAsync("http://localhost:7071/api/login");
        var result = await response.Content.ReadAsStringAsync();
        var connection = JObject.Parse(result);
        using var webSocket = new ClientWebSocket();
        await webSocket.ConnectAsync(new Uri(connection["url"].Value.ToString()), default);
        Console.WriteLine("[Progress] Connected.");
        var ms = new MemoryStream();
        Memory<byte> buffer = new byte[1024];
        // receive loop
        while (true)
        {
            var receiveResult = await webSocket.ReceiveAsync(buffer, default);
            // Need to check again for NetCoreApp2.2 because a close can happen between a 0-byte read and the actual read
            if (receiveResult.MessageType == WebSocketMessageType.Close)
            {
                try
                {
                    await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, default);
                }
                catch
                {
                    // It is possible that the remote is already closed
                }
                break;
            }
            await ms.WriteAsync(buffer.Slice(0, receiveResult.Count));
            if (receiveResult.EndOfMessage)
            {
                Console.WriteLine($"[Received]: {Encoding.UTF8.GetString(ms.ToArray())}");
                ms.SetLength(0);
            }
        }
    }
    

The code above first create a rest call to Azure Function login to retrieve service connection url for client. Then use the url to establish a websocket connection to service. After the connection is established, you’ll be able to receive server side messages.

Further: Set up chat app for a bi-redirection communication

Try with Sample.

Find an issue? Please report it here. Your feedback is highly appreciated! Provide it via Azure Feedback Forum.