In this post, let's look at how we can use multiple output bindings with HTTP-triggered Azure Functions running on the isolated worker model.
When working with HTTP-triggered functions, you often need to return an HTTP response to the caller while also sending data to other services like Azure Service Bus, Azure Queue Storage, etc. This is where multiple output bindings come in handy.
HttpRequestData vs HttpRequest
With Azure Functions isolated worker model, by default we have HttpRequestData and HttpResponseData types from the Microsoft.Azure.Functions.Worker.Extensions.Http package.
However, we can also use ASP.NET Core integration via the Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore NuGet package. This enables using standard ASP.NET Core types including HttpRequest, HttpResponse, and IActionResult in HTTP Triggers.
Let's see how multiple output bindings work with both approaches.
To have multiple outputs, we need to create a class that contains our output
binding properties.
Using HttpRequestData
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using System.Net;
namespace FunctionApp1;
public class HttpStartWithHttpRequestData
{
[Function(nameof(HttpStartWithHttpRequestData))]
public async Task<HttpStartOutputWithHttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
HttpResponseData response = req.CreateResponse(HttpStatusCode.OK);
await response.WriteStringAsync("Message Sent");
return new HttpStartOutputWithHttpResponseData()
{
ServiceBusMessageContent = "Some Content",
HttpResponse = response
};
}
}
public class HttpStartOutputWithHttpResponseData
{
[ServiceBusOutput("sbt-test-topic", ServiceBusEntityType.Topic, Connection = "ServiceBusConnection")]
public string? ServiceBusMessageContent { get; set; }
public HttpResponseData HttpResponse { get; set; }
}
With HttpRequestData, the HttpResponseData property doesn't require any special attribute. The function runtime automatically recognizes it as the HTTP response.
Note: The ServiceBusMessageContent property is nullable. If the value isn't set (i.e., it's null), no
message will be sent to the Service Bus. This allows you to conditionally send
messages based on your business logic.
Using HttpRequest (ASP.NET Core Integration)
When using HttpRequest from the ASP.NET Core integration, things are slightly different. We need to use the [HttpResult] attribute to indicate which property is the HTTP response.
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using System.Net;
namespace FunctionApp1;
public class HttpStartWithHttpRequest
{
[Function(nameof(HttpStartWithHttpRequest))]
public HttpStartOutputWithHttpResponse Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequest req)
{
return new HttpStartOutputWithHttpResponse()
{
ServiceBusMessageContent = "Some Content",
HttpResponse = new ObjectResult("Message Sent")
{
StatusCode = (int)HttpStatusCode.OK
}
};
}
}
public class HttpStartOutputWithHttpResponse
{
[ServiceBusOutput("sbt-test-topic", ServiceBusEntityType.Topic, Connection = "ServiceBusConnection")]
public string? ServiceBusMessageContent { get; set; }
[HttpResult]
public IActionResult HttpResponse { get; set; }
}
The [HttpResult] attribute is required here because without it, the runtime won't know which property represents the HTTP response.
Note: Similar to the previous example, the ServiceBusMessageContent property is nullable. If the value isn't set, no message will be sent to the Service Bus.
Limitations
There's a significant limitation when it comes to Service Bus output bindings in the isolated worker model. The content type has to be the message body itself - it can be a simple type (like string) or a complex type (a POCO).
You cannot use ServiceBusMessage from the Azure.Messaging.ServiceBus SDK as the output type. If you try to do so, the entire ServiceBusMessage object gets serialized as JSON, resulting in something like this:
{
"Body": "eyJDb250ZW50IjoiU29tZSBDb250ZW50In0=",
"MessageId": null,
"PartitionKey": null,
"SessionId": null,
"TimeToLive": "10675199.02:48:05.4775807",
"CorrelationId": null,
"Subject": null,
"ContentType": null,
"ApplicationProperties": {}
}
This means you cannot set message properties like CorrelationId, SessionId, Subject, or ApplicationProperties using output bindings in the isolated worker model. If you need to set these properties, you'll have to use the ServiceBusClient directly.
This is a known limitation and has been discussed in several GitHub issues:
- [FEATURE REQ] Azure Functions Service Bus output - support setting message metadata
- Azure Service Bus output binding for Azure Functions is limited to message body
- Correct usage of ServiceBusOutput in isolated mode
- ServiceBusOutput binding not working in .NET 8 isolated Azure Function, but manual send works
Hope this helps.
Happy Coding.
Regards,
Jaliya