microsoft / botframework-components

The repository for components built by Microsoft for the Azure Bot Framework.
https://aka.ms/botdocs
MIT License
112 stars 82 forks source link

Reusable HttpClient for Custom Actions #1257

Open lshade opened 3 years ago

lshade commented 3 years ago

I have a bot with multiple Custom Actions that each do various HTTP requests. I want to avoid socket exhaustion and use one HttpClient across the whole solution with dependency injection (described here)

Anyone have any pointers on how to accomplish this?

lshade commented 3 years ago

I see now how the HttpClient is disposed in the HttpRequest action code https://github.com/microsoft/botbuilder-dotnet/blob/0e332233cca3b725696c783cb6561cd94a14475b/libraries/Microsoft.Bot.Builder.Dialogs.Adaptive/Actions/HttpRequest.cs#L230

I could try doing the same unless there's an update on the HttpClientFactory option

cwhitten commented 3 years ago

Hi @lshade - transferring your issue to the botframework-components project for discussion on custom action abilities.

johnkm516 commented 2 years ago

@tracyboehrer Is there an update on this? I have the same question.

tracyboehrer commented 2 years ago

There isn't an official solution to this yet. However, there is a way to work around it.

This requires that HttpClient is added to DI in Startup, and a new impl of Middleware is injected that will add an HttpClient to the TurnState on each turn. The HttpRequest Action will use that HttpClient in the TurnState.

Startup.cs

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers().AddNewtonsoftJson();

            services.AddHttpClient();
            services.AddSingleton<IMiddleware, HttpClientMiddleware>();

            services.AddBotRuntime(Configuration);
        }

Then create HttpClientMiddlware.cs

using Microsoft.Bot.Builder;
using System.Threading.Tasks;
using System.Threading;
using System;
using System.Net.Http;
using Microsoft.AspNetCore.Http;

namespace {your_bot_namespace_goes_here}
{
    public class HttpClientMiddleware : Microsoft.Bot.Builder.IMiddleware
    {
        private readonly IHttpClientFactory _factory;

        public HttpClientMiddleware(IHttpClientFactory factory)
        {
            _factory = factory ?? throw new ArgumentNullException(nameof(factory));
        }

        public Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default)
        {
            turnContext.TurnState.Add(_factory.CreateClient(nameof(HttpRequest)));
            return next(cancellationToken);
        }
    }
}