What's the recommended way to use http client in durable function (premium plan)

sunny 40 Reputation points
2024-01-16T06:31:04.56+00:00

I knew the recommended way to implement the HttpClient is using IHttpClientFactory. In normal function app, the HttpClient can be registered in the startup and can be requested using DI. but I am not sure how to implement it in below durable function.

    public static class AttachmentTransfer
    {
        [FunctionName("AttachmentTransfer")]
        public static async Task<string> RunOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            // Get the URL and destination API URL from the input
            AttachmentTransferRequest attachmentRequest = context.GetInput<AttachmentTransferRequest>();

            //Download and Upload file
            string outputs = await context.CallActivityAsync<string>("DownloadUploadFileActivity", attachmentRequest);

            return outputs;
        }

        [FunctionName("AttachmentTransfer_HttpStart")]
        public static async Task<HttpResponseMessage> HttpStart(
            [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestMessage req,
            [DurableClient] IDurableOrchestrationClient starter,
            ILogger log)
        {
            var data = await req.Content.ReadAsAsync<AttachmentTransferRequest>();
            // Function input comes from the request content.
            string instanceId = await starter.StartNewAsync<AttachmentTransferRequest>("AttachmentTransfer", data);

            log.LogInformation("Started orchestration with ID = '{instanceId}'.", instanceId);

            return starter.CreateCheckStatusResponse(req, instanceId);
        }

        [FunctionName("DownloadUploadFileActivity")]
        public static async Task<string> DownloadUploadFileActivity(
[ActivityTrigger] AttachmentTransferRequest request,
ILogger log)
        {
            log.LogInformation("Started DownloadUploadFileActivity");
            using (HttpClient client = new HttpClient())
            {
                // download and upload
            }
        }
    }

Azure Functions
Azure Functions
An Azure service that provides an event-driven serverless compute platform.
5,911 questions
{count} votes

Accepted answer
  1. MikeUrnun 9,777 Reputation points Moderator
    2024-01-24T00:03:34.89+00:00

    Hello @sunny - Thanks for providing the details via comments. I tried a similar use case (on .NET8 isolated-worker) and have configured the following where the HttpClient is added via DI and tested by using it to download a .csv file data from an HTTP endpoint:
    Program.cs

    using Microsoft.Azure.Functions.Worker;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    
    var host = new HostBuilder()
        .ConfigureFunctionsWorkerDefaults()
        .ConfigureServices(services =>
        {
            services.AddApplicationInsightsTelemetryWorkerService();
            services.ConfigureFunctionsApplicationInsights();
            services.AddHttpClient();
        })
        .Build();
    
    host.Run();
    

    Function1.cs

    using Microsoft.Azure.Functions.Worker;
    using Microsoft.Azure.Functions.Worker.Http;
    using Microsoft.DurableTask;
    using Microsoft.DurableTask.Client;
    using Microsoft.Extensions.Logging;
    
    namespace FunctionApp8
    {
        public class Function1
        {
            private readonly HttpClient httpClient;
            public Function1(IHttpClientFactory factory)
            {
                httpClient = factory.CreateClient();
            }
    
            [Function(nameof(Function1))]
            public static async Task<List<string>> RunOrchestrator(
                [OrchestrationTrigger] TaskOrchestrationContext context)
            {
                ILogger logger = context.CreateReplaySafeLogger(nameof(Function1));
                logger.LogInformation("Saying hello.");
                var outputs = new List<string>();
    
                // Replace name and input with values relevant for your Durable Functions Activity
                outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "Tokyo"));
                return outputs;
            }
    
            [Function(nameof(SayHello))]
            public async Task<string> SayHello([ActivityTrigger] string name, FunctionContext executionContext)
            {
                ILogger logger = executionContext.GetLogger("SayHello");
                logger.LogInformation("Saying hello to {name}.", name);
                using HttpResponseMessage response = await httpClient.GetAsync("https://murnunstorage.blob.core.windows.net/subfolder/Integration.csv");
                string responseBody = await response.Content.ReadAsStringAsync();
                return responseBody;
            }
    
            [Function("Function1_HttpStart")]
            public static async Task<HttpResponseData> HttpStart(
                [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
                [DurableClient] DurableTaskClient client,
                FunctionContext executionContext)
            {
                ILogger logger = executionContext.GetLogger("Function1_HttpStart");
    
                // Function input comes from the request content.
                string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
                    nameof(Function1));
    
                logger.LogInformation("Started orchestration with ID = '{instanceId}'.", instanceId);
    
                // Returns an HTTP 202 response with an instance management payload.
                // See https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-http-api#start-orchestration
                return client.CreateCheckStatusResponse(req, instanceId);
            }
        }
    }
    
    
    

    The above is of the same structure as yours (Http starter -> Orchestration -> Activitiy), and after the Activity function is run, I confirmed successful access to the response (downloaded contents of the remote file):
    User's image

    Could you try the same way as above and let us know if it helps accomplish your use case? I look forward to your reply.


    Please "Accept Answer" if the answer is helpful so that others in the community may benefit from your experience.

    3 people found this answer helpful.

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.