On one of my recent projects, a client application was required to place a message onto an Azure Service Bus by using a HTTP endpoint rather than using the Service Bus SDK and with the following constraints.
- The client is unable to generate the Service Bus SAS token.
- Service Bus Session Id needs to be set to the customer number found in the message to ensure ordered delivery by the consumer.
- All messages are to be sent to a Service Bus Topic and require to add a custom Service Bus property called ‘MgsType’.
- Custom HTTP Request Headers may be used.
I decided upon a solution that uses Azure APIM to expose the Service Bus endpoint. A custom policy will be used to generate the Service Bus SAS token and to parse the message for the customer Id. The customer Id will then be used to set the SessionId on the Service Bus. The client then only has to register for a subscription key in the Azure Developer portal and then pass this key with each HTTP request in the header.
Next add a subscription to the topic with ‘Require Session’ enabled and the following rule “MsgType = ‘Deposits’”
Before we start developing the custom policies, we need to setup 3 Name Values in the APIM blade as shown below. These values are used for generating the Service Bus SAS token and are obtained from the Service Bus properties.
- SB_Key – primary key for the ‘Sender’ shared access policies
- SB_KeyName – name of the Shared access policy
- SB_Uri – is the topic URL which can be found by clicking on the topic name under the ‘Topics’ blade shown below.
Next is to create an API using the ‘Blank API’ template similar to what I have done below. Note the ‘Web service URL’ value is the base address of the Service Bus topic URL. (ie without the topic name resource location)
The APIM policy consists of several code blocks inside the inbound processing stage which are describe in detail below. To improve performance I will be caching the generated SAS token.
The first code block looks up cache for a value. If nothing is found then the variable “cachedSasToken” is assigned to null as there is no default value specified.
Next a control flow is used to check the variable “cachedSasToken” for null and if true then a SAS token is generated using the values stored in APIM Name-Value pairs. Once the token is calculated then it is stored in cache and set to expire in 120 seconds. The cache is then read again to assign the variable “cachedSasToken” with the generated SAS token.
Both the signature and resource URL is required to be UrlEncoded. My first choice was to use the System.Web.UrlEncode function to encode the values. Unfortunately this function is not available in the APIM policy expressions as it only has a subset of the .Net Framework types. To work around this issue, I ended up using the System.Uri.EscapeDataString method instead.
The SAS token is then added to the header using the value from the variable ‘cachedSasToken’
I then set the message content type to application/json and remove the APIM subscription key header from being sent to the Service Bus endpoint.
The last part is to extract the customer number from the message and assign it to the SessionId property of the Service Bus. The standard set of Service Bus properties are required to be added to a custom header called ‘BrokerProperties’
The full code for the custom policy is here:
Now lets use Postman to send a message to the URL endpoint exposed by APIM to test the policy. The headers contain the APIM subscription key and a custom header for the ‘MsgType’ which will be used for the Service Bus subscription filter.
The message body simply contains the Customer number and the amount to deposit.
After posting the message to the APIM endpoint URL, we can see the message was successfully forwarded to the Service Bus by using Service Bus Explorer to view the message properties and content.