How to make a bare minimum Serverless SingnalR api & client

68 views Asked by At

How do you make a bare-bones Serverless SingnalR app? The docs on this are really scattered, and it seems like all the trad. asp versions keep saying, "Dont worry about this stuff," which is required for serverless.

This works for Negotiation, but what am I missing after that? The calls to the api do not hit endpoints. I am only running this locally. This app does work as a server responding to http requests, without the SignalR stuff, so I can hit some endpoints.

Backend code:

// This gets hit and succeeds:
  [FunctionName("negotiate")]
  public static SignalRConnectionInfo Negotiate(
      [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
      [SignalRConnectionInfo(HubName = "TestHub")] SignalRConnectionInfo connectionInfo)
  {
    return connectionInfo;
  }

// This works in an ASP app, but doesn't get hit in Serverless:
  public async Task SendMessage(string message)
  {
    char[] stringArray = message.ToCharArray();
    Array.Reverse(stringArray);
    string reversedStr = new string(stringArray);

    await Clients.All.SendAsync("ReceiveMessage", reversedStr);
  }

Frontend code:

// All this works for Vue talking to the ASP app, and it hits Negotiate with serverless, but noting else:

const connection = new signalR.HubConnectionBuilder()
  //.withUrl("http://localhost:5192/chatHub") //SignalR-POC.ASP
  .withUrl("http://localhost:7230/api") //Serverless running locally
  .configureLogging(signalR.LogLevel.Information)
  .withAutomaticReconnect()
  .build();
    
// Inbound: `connection.on()`s should be registered before `.start` executes
connection.on("ReceiveMessage", (reply) => {
  console.log("[SignalR.ReceiveMessage] " + reply);
  serverReply.value = reply;
});

async function start() {
  try {
    await connection.start();
    console.log("[SignalR.start] Connected.");
  } catch (err) {
    console.log(err);
    setTimeout(start, 5000);
  }    
};
  
// Outbound
async function sendToServer(){
  try {
    console.log("[SignalR.SendMessage] " + userInput.value);
    await connection.invoke("SendMessage", userInput.value);
  } catch (err) {
    console.error(err);
  }
}


connection.onclose( async () => {
  console.log("[SignalR.onclose] Closed.");
  await start() 
});


// Start the connection.
start();
</script>
<style>
.table2x3{
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(3, 1fr);
}
.my-button{
  grid-column-end: span 2; 
  margin-top:0.5em;
}



1

There are 1 answers

0
SaiVamshiKrishna On

Here is the way i am able to run Serverless SingnalR api & client locally and able to hit the endpoint

1. Serverless SignalR API:

  • Choose Azure Functions and create the project.
  • Right-click the project > Add > New Azure Function
  • Choose HTTP trigger and provide a name (e.g., "HttpFunction").

Here is the function code

    using Microsoft.AspNetCore.Http;
    using Microsoft.Azure.WebJobs;
    using Microsoft.Azure.WebJobs.Extensions.Http;
    using Microsoft.Extensions.Logging;
    using Microsoft.Azure.WebJobs.Extensions.SignalRService;
    using System.IO;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    
    public static class HttpFunction
    {
        [FunctionName("SendMessage")]
        public static async Task<IActionResult> SendMessage(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            [SignalR(HubName = "chat")] IAsyncCollector<SignalRMessage> signalRMessages,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");
    
            string name = req.Query["name"];
            string message = req.Query["message"];
    
            if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(message))
            {
                var signalRMessage = new SignalRMessage
                {
                    Target = "newMessage",
                    Arguments = new object[] { name, message }
                };
                await signalRMessages.AddAsync(signalRMessage);
                return new OkObjectResult($"Message sent to SignalR hub");
            }
            else
            {
                return new BadRequestObjectResult("Please pass a name and message in the query string");
            }
        }
    }

2. Serverless SignalR Hub: Create a new SignalR hub by adding a new class (e.g., ChatHub.cs) to your project:

    using Microsoft.AspNetCore.SignalR;
    
    public class ChatHub : Hub
    {
        public async Task SendMessage(string name)
        {
            await Clients.All.SendAsync("newMessage", name);
        }
    }

3.Configure SignalR Connection String:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "AzureSignalRConnectionString": "YourAzureSignalRConnectionString"
  },
  "Host": {
    "LocalHttpPort": 7071,
    "CORS": "http://localhost:7071/api/SendMessage" // Add this line to specify allowed origins (e.g., localhost:3000)
  }
}

4.Client:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>SignalR Client</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.11/signalr.min.js"></script>
</head>
<body>
    <h1>SignalR Client</h1>
    <input type="text" id="messageInput" placeholder="Enter your name">
    <button id="sendMessage">Send</button>
    <ul id="messages"></ul>

    <script>
        const connection = new signalR.HubConnectionBuilder()
            .withUrl("/api")
            .build();

        connection.start().catch(function (err) {
            return console.error(err.toString());
        });

        document.getElementById("sendMessage").addEventListener("click", function () {
            const name = document.getElementById("messageInput").value;
            connection.invoke("SendMessage", name).catch(function (err) {
                return console.error(err.toString());
            });
        });

        connection.on("newMessage", function (name) {
            const li = document.createElement("li");
            li.textContent = name;
            document.getElementById("messages").appendChild(li);
        });
    </script>
</body>
</html>

5. Run the Solution Function Triggered enter image description here

Result Using Postman Select Post,Paste your api and Click Send enter image description here

Pass your values enter image description here

Result enter image description here