title | description | author | ms.service | ms.devlang | ms.topic | ms.custom | ms.date | ms.author |
---|---|---|---|---|---|---|---|---|
Azure SignalR Service serverless quickstart - C# | A quickstart for using Azure SignalR Service and Azure Functions to create an app showing GitHub star count using C#. | vicancy | azure-signalr-service | csharp | quickstart | devx-track-csharp, mode-other | 05/16/2024 | lianwei |
In this article, you learn how to use SignalR Service and Azure Functions to build a serverless application with C# to broadcast messages to clients.
Note
You can get the code mentioned in this article from GitHub.
Note
You can get the code mentioned in this article from GitHub.
[!INCLUDE Connection string security]
The following prerequisites are needed for this quickstart:
- Visual Studio Code, or other code editor. If you don't already have Visual Studio Code installed, download Visual Studio Code here.
- An Azure subscription. If you don't have an Azure subscription, create one for free before you begin.
- Azure Functions Core Tools
- .NET Core SDK
[!INCLUDE Create instance]
You need the Azure Functions Core Tools for this step.
Create an empty directory and change to the directory with the command line.
Initialize a new project.
# Initialize a function project func init --worker-runtime dotnet # Add SignalR Service package reference to the project dotnet add package Microsoft.Azure.WebJobs.Extensions.SignalRService
# Initialize a function project func init --worker-runtime dotnet-isolated # Add extensions package references to the project dotnet add package Microsoft.Azure.Functions.Worker.Extensions.Http dotnet add package Microsoft.Azure.Functions.Worker.Extensions.SignalRService dotnet add package Microsoft.Azure.Functions.Worker.Extensions.Timer
Using your code editor, create a new file with the name Function.cs. Add the following code to Function.cs:
usingSystem;usingSystem.IO;usingSystem.Linq;usingSystem.Net.Http;usingSystem.Threading.Tasks;usingMicrosoft.AspNetCore.Http;usingMicrosoft.AspNetCore.Mvc;usingMicrosoft.Azure.WebJobs;usingMicrosoft.Azure.WebJobs.Extensions.Http;usingMicrosoft.Azure.WebJobs.Extensions.SignalRService;usingNewtonsoft.Json;namespaceCSharp{publicstaticclassFunction{privatestaticHttpClienthttpClient=newHttpClient();privatestaticstringEtag=string.Empty;privatestaticstringStarCount="0";[FunctionName("index")]publicstaticIActionResultGetHomePage([HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequestreq,ExecutionContextcontext){varpath=Path.Combine(context.FunctionAppDirectory,"content","index.html");returnnewContentResult{Content=File.ReadAllText(path),ContentType="text/html",};}[FunctionName("negotiate")]publicstaticSignalRConnectionInfoNegotiate([HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequestreq,[SignalRConnectionInfo(HubName="serverless")]SignalRConnectionInfoconnectionInfo){returnconnectionInfo;}[FunctionName("broadcast")]publicstaticasyncTaskBroadcast([TimerTrigger("*/5 * * * * *")]TimerInfomyTimer,[SignalR(HubName="serverless")]IAsyncCollector<SignalRMessage>signalRMessages){varrequest=newHttpRequestMessage(HttpMethod.Get,"https://api.github.com/repos/azure/azure-signalr");request.Headers.UserAgent.ParseAdd("Serverless");request.Headers.Add("If-None-Match",Etag);varresponse=awaithttpClient.SendAsync(request);if(response.Headers.Contains("Etag")){Etag=response.Headers.GetValues("Etag").First();}if(response.StatusCode==System.Net.HttpStatusCode.OK){varresult=JsonConvert.DeserializeObject<GitResult>(awaitresponse.Content.ReadAsStringAsync());StarCount=result.StarCount;}awaitsignalRMessages.AddAsync(newSignalRMessage{Target="newMessage",Arguments=new[]{$"Current star count of https://github.com/Azure/azure-signalr is: {StarCount}"}});}privateclassGitResult{[JsonRequired][JsonProperty("stargazers_count")]publicstringStarCount{get;set;}}}}
usingSystem.Net;usingSystem.Net.Http.Json;usingSystem.Text.Json.Serialization;usingMicrosoft.Azure.Functions.Worker;usingMicrosoft.Azure.Functions.Worker.Http;namespacecsharp_isolated;publicclassFunctions{privatestaticreadonlyHttpClientHttpClient=new();privatestaticstringEtag=string.Empty;privatestaticintStarCount=0;[Function("index")]publicstaticHttpResponseDataGetHomePage([HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequestDatareq){varresponse=req.CreateResponse(HttpStatusCode.OK);response.WriteString(File.ReadAllText("content/index.html"));response.Headers.Add("Content-Type","text/html");returnresponse;}[Function("negotiate")]publicstaticHttpResponseDataNegotiate([HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequestDatareq,[SignalRConnectionInfoInput(HubName="serverless")]stringconnectionInfo){varresponse=req.CreateResponse(HttpStatusCode.OK);response.Headers.Add("Content-Type","application/json");response.WriteString(connectionInfo);returnresponse;}[Function("broadcast")][SignalROutput(HubName="serverless")]publicstaticasyncTask<SignalRMessageAction>Broadcast([TimerTrigger("*/5 * * * * *")]TimerInfotimerInfo){varrequest=newHttpRequestMessage(HttpMethod.Get,"https://api.github.com/repos/azure/azure-signalr");request.Headers.UserAgent.ParseAdd("Serverless");request.Headers.Add("If-None-Match",Etag);varresponse=awaitHttpClient.SendAsync(request);if(response.Headers.Contains("Etag")){Etag=response.Headers.GetValues("Etag").First();}if(response.StatusCode==HttpStatusCode.OK){varresult=awaitresponse.Content.ReadFromJsonAsync<GitResult>();if(result!=null){StarCount=result.StarCount;}}returnnewSignalRMessageAction("newMessage",newobject[]{$"Current star count of https://github.com/Azure/azure-signalr is: {StarCount}"});}privateclassGitResult{[JsonPropertyName("stargazers_count")]publicintStarCount{get;set;}}}
The code in Function.cs has three functions:
GetHomePage
is used to get a website as client.Negotiate
is used by the client to get an access token.Broadcast
is periodically called to get the star count from GitHub and then broadcast messages to all clients.
The client interface for this sample is a web page. We render the web page using the
GetHomePage
function by reading HTML content from file content/index.html. Now let's create this index.html under thecontent
subdirectory with the following content:<html><body><h1>Azure SignalR Serverless Sample</h1><divid="messages"></div><scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.7/signalr.min.js"></script><script>letmessages=document.querySelector('#messages');constapiBaseUrl=window.location.origin;constconnection=newsignalR.HubConnectionBuilder().withUrl(apiBaseUrl+'/api').configureLogging(signalR.LogLevel.Information).build();connection.on('newMessage',(message)=>{document.getElementById("messages").innerHTML=message;});connection.start().catch(console.error);</script></body></html>
Update your
*.csproj
to make the content page in the build output folder.<ItemGroup><NoneUpdate="content/index.html"><CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory></None></ItemGroup>
Azure Functions requires a storage account to work. You can install and run the Azure Storage Emulator. Or you can update the setting to use your real storage account with the following command:
func settings add AzureWebJobsStorage "<storage-connection-string>"
It's almost done now. The last step is to set a connection string of the SignalR Service to Azure Function settings.
Confirm the SignalR Service instance was successfully created by searching for its name in the search box at the top of the portal. Select the instance to open it.
Select Keys to view the connection strings for the SignalR Service instance.
Copy the primary connection string, and then run the following command.
[!INCLUDE Connection string security comment]
func settings add AzureSignalRConnectionString "<signalr-connection-string>"
Run the Azure function locally:
func start
After the Azure function is running locally, open
http://localhost:7071/api/index
, and you can see the current star count. If you star or unstar in the GitHub, you get a star count refreshing every few seconds.
[!INCLUDE Cleanup]
Having issues? Try the troubleshooting guide or let us know.
In this quickstart, you built and ran a real-time serverless application locally. Next, learn more about bi-directional communication between clients and Azure Functions with Azure SignalR Service.
[!div class="nextstepaction"] SignalR Service bindings for Azure Functions
[!div class="nextstepaction"] Azure Functions Bi-directional communicating sample
[!div class="nextstepaction"] Azure Functions Bi-directional communicating sample for isolated process
[!div class="nextstepaction"] Deploy to Azure Function App using Visual Studio