Reputation: 1148
Like many I'm using the HttpClient in some of my Azure functions (v3.x). The issue is that Azure function host uses the HttpClient as a singleton. This means that if I set the headers in one function they're set for all functions. I want to have different headers (e.g. Authorization headers) in different functions. Is the only way to accomplish this is to create different function apps for each different set of headers that I need?
Upvotes: 8
Views: 8676
Reputation: 13527
You typically don't use a "global" HttpClient
's DefaultRequestHeaders
. You would want to create an HttpRequestMessage
and use SendAsync
. This gives that session a unique header dictionary that is segregated from all other requests.
HttpClient
's DefaultRequestHeaders
are not thread-safe. So if you modify them in the middle of another request, you will crash.
What you should do is inject HttpClientFactory
and then create clients from that. Continue to use the HttpRequestMessage
/SendAsync
pattern (or use straight-up GetAsync
, PostAsync
, etc.) all while avoiding DefaultRequestHeaders
(if you use the factory's CreateClient()
method).
If you use a factory, you can use DefaultRequestHeaders
if you create a named or typed HttpClient
object from the factory. You will probably want to do this for your specific use-case you mentioned in your question.
Microsoft.Azure.Functions.Extensions
and Microsoft.Extensions.Http
packages via NuGet.Startup.cs
. Make it look like this:using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using System.Net.Http.Headers;
[assembly: FunctionsStartup(typeof(MyAzureFunction.Startup))]
namespace MyAzureFunction
{
public sealed class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddHttpClient("MyAuthorizedClient", client =>
{
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", "<token>");
});
}
}
}
static
. Make sure to remove the static
keyword. Then, add a constructor. We will inject the factory via constructor:using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace MyAzureFunction
{
public sealed class Function1
{
private readonly IHttpClientFactory _httpClientFactory;
public Function1(IHttpClientFactory httpContextFactory)
{
_httpClientFactory = httpContextFactory;
}
[FunctionName("Function1")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log)
{
//
// make a request on the authorized client
var authorizedClient = _httpClientFactory.CreateClient("MyAuthorizedClient");
using (var response = await authorizedClient
.GetAsync(new Uri("https://www.example.com")))
{
var objRecved = await response.Content.ReadAsAsync<object>();
return new OkObjectResult(new { data = objRecved });
}
//
// make a request on the non-authorized client using it's own header collection
var nonAuthorizedClient = _httpClientFactory.CreateClient();
using (var request = new HttpRequestMessage(
HttpMethod.Get, new Uri("https://www.example.com")))
{
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", "<token>");
using(var response = await nonAuthorizedClient.SendAsync(request))
{
response.EnsureSuccessStatusCode();
var objRecved = await response.Content.ReadAsAsync<object>();
return new OkObjectResult(new { data = objRecved });
}
}
}
}
}
Upvotes: 8