title | description | author | ms.topic | ms.custom | ms.date | ms.author |
---|---|---|---|---|---|---|
Azure Functions SignalR Service trigger binding |
Learn to send SignalR Service messages from Azure Functions. |
chenyl |
reference |
devx-track-csharp |
05/11/2020 |
chenyl |
Use the SignalR trigger binding to respond to messages sent from Azure SignalR Service. When function is triggered, messages passed to the function is parsed as a json object.
In SignalR Service serverless mode, SignalR Service uses the Upstream feature to send messages from client to Function App. And Function App uses SignalR Service trigger binding to handle these messages. The general architecture is shown below: :::image type="content" source="media/functions-bindings-signalr-service/signalr-trigger.png" alt-text="SignalR Trigger Architecture":::
For information on setup and configuration details, see the overview.
The following example shows a function that receives a message using the trigger binding and log the message.
SignalR Service trigger binding for C# has two programming models. Class based model and traditional model. Class based model can provide a consistent SignalR server-side programming experience. And traditional model provides more flexibility and similar with other function bindings.
See Class based model for details.
public class SignalRTestHub : ServerlessHub
{
[FunctionName("SignalRTest")]
public async Task SendMessage([SignalRTrigger]InvocationContext invocationContext, string message, ILogger logger)
{
logger.LogInformation($"Receive {message} from {invocationContext.ConnectionId}.");
}
}
Traditional model obeys the convention of Azure Function developed by C#. If you're not familiar with it, you can learn from documents.
[FunctionName("SignalRTest")]
public static async Task Run([SignalRTrigger("SignalRTest", "messages", "SendMessage", parameterNames: new string[] {"message"})]InvocationContext invocationContext, string message, ILogger logger)
{
logger.LogInformation($"Receive {message} from {invocationContext.ConnectionId}.");
}
As it's bit cumbersome to use ParameterNames
, SignalRParameter
is provided to achieve the same purpose.
[FunctionName("SignalRTest")]
public static async Task Run([SignalRTrigger("SignalRTest", "messages", "SendMessage")]InvocationContext invocationContext, [SignalRParameter]string message, ILogger logger)
{
logger.LogInformation($"Receive {message} from {invocationContext.ConnectionId}.");
}
Here's binding data in the function.json file:
Example function.json:
{
"type": "signalRTrigger",
"name": "invocation",
"hubName": "SignalRTest",
"category": "messages",
"event": "SendMessage",
"parameterNames": [
"message"
],
"direction": "in"
}
Here's the C# Script code:
#r "Microsoft.Azure.WebJobs.Extensions.SignalRService"
using System;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using Microsoft.Extensions.Logging;
public static void Run(InvocationContext invocation, string message, ILogger logger)
{
logger.LogInformation($"Receive {message} from {invocationContext.ConnectionId}.");
}
Here's binding data in the function.json file:
Example function.json:
{
"type": "signalRTrigger",
"name": "invocation",
"hubName": "SignalRTest",
"category": "messages",
"event": "SendMessage",
"parameterNames": [
"message"
],
"direction": "in"
}
Here's the JavaScript code:
module.exports = function (context, invocation) {
context.log(`Receive ${context.bindingData.message} from ${invocation.ConnectionId}.`)
context.done();
};
Here's binding data in the function.json file:
Example function.json:
{
"type": "signalRTrigger",
"name": "invocation",
"hubName": "SignalRTest",
"category": "messages",
"event": "SendMessage",
"parameterNames": [
"message"
],
"direction": "in"
}
Here's the Python code:
import logging
import json
import azure.functions as func
def main(invocation) -> None:
invocation_json = json.loads(invocation)
logging.info("Receive {0} from {1}".format(invocation_json['Arguments'][0], invocation_json['ConnectionId']))
The following table explains the binding configuration properties that you set in the function.json file and the SignalRTrigger
attribute.
function.json property | Attribute property | Description |
---|---|---|
type | n/a | Must be set to SignalRTrigger . |
direction | n/a | Must be set to in . |
name | n/a | Variable name used in function code for trigger invocation context object. |
hubName | HubName | This value must be set to the name of the SignalR hub for the function to be triggered. |
category | Category | This value must be set as the category of messages for the function to be triggered. The category can be one of the following values:
|
event | Event | This value must be set as the event of messages for the function to be triggered. For messages category, event is the target in invocation message that clients send. For connections category, only connected and disconnected is used. |
parameterNames | ParameterNames | (Optional) A list of names that binds to the parameters. |
connectionStringSetting | ConnectionStringSetting | The name of the app setting that contains the SignalR Service connection string (defaults to "AzureSignalRConnectionString") |
The trigger input type is declared as either InvocationContext
or a custom type. If you choose InvocationContext
you get full access to the request content. For a custom type, the runtime tries to parse the JSON request body to set the object properties.
InvocationContext contains all the content in the message send from SignalR Service.
Property in InvocationContext | Description |
---|---|
Arguments | Available for messages category. Contains arguments in invocation message |
Error | Available for disconnected event. It can be Empty if the connection closed with no error, or it contains the error messages. |
Hub | The hub name which the message belongs to. |
Category | The category of the message. |
Event | The event of the message. |
ConnectionId | The connection ID of the client which sends the message. |
UserId | The user identity of the client which sends the message. |
Headers | The headers of the request. |
Query | The query of the request when clients connect to the service. |
Claims | The claims of the client. |
The property ParameterNames
in SignalRTrigger
allows you to bind arguments of invocation messages to the parameters of functions. The name you defined can be used as part of binding expressions in other binding or as parameters in your code. That gives you a more convenient way to access arguments of InvocationContext
.
Say you have a JavaScript SignalR client trying to invoke method broadcast
in Azure Function with two arguments message1
, message2
.
await connection.invoke("broadcast", message1, message2);
After you set parameterNames
, the name you defined will respectively correspond to the arguments sent on the client side.
[SignalRTrigger(parameterNames: new string[] {"arg1, arg2"})]
Then, the arg1
will contain the content of message1
, and arg2
will contain the content of message2
.
For the parameter binding, the order matters. If you are using ParameterNames
, the order in ParameterNames
matches the order of the arguments you invoke in the client. If you are using attribute [SignalRParameter]
in C#, the order of arguments in Azure Function methods matches the order of arguments in clients.
ParameterNames
and attribute [SignalRParameter]
cannot be used at the same time, or you will get an exception.
SignalR Service needs a URL to access Function App when you're using SignalR Service trigger binding. The URL should be configured in Upstream Settings on the SignalR Service side.
:::image type="content" source="../azure-signalr/media/concept-upstream/upstream-portal.png" alt-text="Upstream Portal":::
When using SignalR Service trigger, the URL can be simple and formatted as shown below:
<Function_App_URL>/runtime/webhooks/signalr?code=<API_KEY>
The Function_App_URL
can be found on Function App's Overview page and The API_KEY
is generated by Azure Function. You can get the API_KEY
from signalr_extension
in the App keys blade of Function App.
:::image type="content" source="media/functions-bindings-signalr-service/signalr-keys.png" alt-text="API key":::
If you want to use more than one Function App together with one SignalR Service, upstream can also support complex routing rules. Find more details at Upstream settings.
You can follow the sample in GitHub to deploy a chat room on Function App with SignalR Service trigger binding and upstream feature: Bidirectional chat room sample