Community Guide: Chat Server Prototype

NOTE: The code snippets for this Community Guide are provided at the end of this document.

INTRODUCTION

This guide shows you how to implement an HTTP triggered Azure Function that provides a simple Chat Server prototype. It implements an HTTP triggered Azure Function that receives a new text string as input from the web client and then writes that text string to an Azure Storage Table. It then collects all the existing text strings that have accumulated up to that point in that table and sends them all back as a one concatenated string back to the client. If multiple clients hit this Azure Function, everyone will see all the text messages that have been submitted by all client apps up to that point in time.

This is a simple starter prototype that focuses on triggers and bindings, and we only demonstrate its operation within the Azure Functions page in the Azure Portal. You can develop it further on your own to work with an actual web browser app or a mobile device app on the client side. To do it properly, you would need to return a text string that formats all the text messages from the table into an actual HTML5 fragment, such as list items in an ordered list element or as rows in a table element. You would also want to create the necessary client-side code to interact with this Azure Function and display the results, perhaps using something like jQuery. However, this is outside the scope of this guide. In this example, we only return a simple new-line delimited list of concatenated text messages that we view in the Azure Functions Output panel in the Azure Portal.

STEP-BY-STEP INSTRUCTIONS

1.Create an HTTP triggered Azure Function, and edit the code in the Run.csx file, as shown at the end of this document.

2.Define the required triggers and bindings for this Azure Function in the function.json file, as shown at the end of this document.

3.In the Azure Functions page on the Azure Portal, go to the HTTP method menu in the upper right, and select the POST option.

4.For the Request body, provide the following JSON encoded data:

{
"message": "Azure"
}

5.Click the Run button, and you should see “Azure\n” appear in the Output panel.

6.Click the Run button again, and you should see “Azure\nAzure\n” appear in the Output panel.

7.If you change the text in the JSON object’s message property, you will see that text string ends up within the text displayed in the Output panel as well. The order is somewhat out of sequence, but that can be fixed by sorting the items by timestamp rather than just calling the ToList directly on the table contents – a LINQ query can be used to accomplish this. But we are just keeping the example super simple here to focus on triggers and bindings.

EXPLANATION OF THE RUN.CSX FILE

The Run function receives the following parameters:

•HttpRequestMessage req

•ICollector<MyMessageEntity> outputTable

•IQueryable<MyMessageEntity> inputTable

•TraceWriter log

The req parameter provides the input from the HTTP trigger that contains a new text message string to be added to the table. The outputTable parameter provides the output binding to the table where we write the new text message. The inputTable parameter provides the input from the same table from which we read all existing messages. Other clients may have added more messages recently.

We call the ReadAsAsync method to read the HTTP request message body content as input. We then extract the message from the message body, and we call the Add method to add it to the table by way of the outputTable parameter.

Finally, we iterate over all the existing messages in the table using the inputTable parameter and build up a concatenated string named concatenatedMessages. The result is then sent back to the client by calling the CreateResponse method.

EXPLANATION OF THE FUNCTION.JSON FILE

The function.json file defines the following bindings:

An input HTTP trigger binding named req that only supports the POST HTTP method.

An output HTTP binding named named $return.

An input table binding named inputTable that binds to a table named MyMessageTable.

An output table binding named outputTable that binds to a table named MyMessageTable.

NOTE: The connection string is shown as “AzureWebJobsStorage”, but this may be changed to any storage account that you have available.

CONCLUSION

This community guide has described a simple code example where input and output bindings can be used to implement a simple prototype that can be developed further into a fully functional Chat Server that could be used in a collaborative web app.

THE RUN.CSX FILE
// Run.csx 
#r "Microsoft.WindowsAzure.Storage"
using System.Net;
using Microsoft.WindowsAzure.Storage.Table;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, ICollector<MyMessageEntity> outputTable, IQueryable<MyMessageEntity> inputTable, TraceWriter log)
{
// Get request body
dynamic data = await req.Content.ReadAsAsync<object>();
// Set name to query string or body data
string message = data?.message;
// add message to table
outputTable.Add(
new MyMessageEntity() {
PartitionKey = "My Message Entities",
RowKey = Guid.NewGuid().ToString(),
Message = message
});
// get concatenated string containing all messages in table seperated by new lines
string concatenatedMessages = "";
foreach (MyMessageEntity messageEntity in inputTable.ToList())
{
concatenatedMessages += messageEntity.Message + "\n";
}
// return HTTP response containing concatenated string containing all messages
return message == null
? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a message in the request body")
: req.CreateResponse(HttpStatusCode.OK, concatenatedMessages);
}
public class MyMessageEntity : TableEntity
{
public string Message { get; set; }
}

THE FUNCTION.JSON FILE
{
"bindings": [
{
"authLevel": "function",
"name": "req",
"type": "httpTrigger",
"direction": "in",
"methods": [
"post"
] },
{
"name": "$return",
"type": "http",
"direction": "out"
},
{
"type": "table",
"name": "outputTable",
"tableName": "MyMessageTable",
"connection": "AzureWebJobsStorage",
"direction": "out"
},
{
"type": "table",
"name": "inputTable",
"tableName": "MyMessageTable",
"take": 50,
"connection": "AzureWebJobsStorage",
"direction": "in"
}
],
"disabled": false
}

Looking for team training?