ConfigureFunctionsWorkerDefaults vs ConfigureFunctionsWebApplication in .NET Azure Functions
When you're creating a .NET Azure Function using the isolated worker model, the default template creates code that has the host builder configuration calling ConfigureFunctionsWorkerDefaults
.
But if you want to enable ASP.NET Core integration features (which you would do if you wanted to work directly with HttpRequest
, HttpResponse
, and IActionResult
types in your functions), then the documentation suggests to instead call ConfigureFunctionsWebApplication.
Usefully, once you add a package reference to Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore, you'll also get a .NET code analyzer error AZFW0014 if you're not calling ConfigureFunctionsWebApplication
, so the compiler will ensure you're doing the right thing.
But apart from the integration with ASP.NET Core, are there any other significant differences between calling the two methods?
Because the code is hosted on GitHub, we can easily review the source code to find out.
ConfigureFunctionsWorkerDefaults
ConfigureFunctionsWorkerDefaults
is defined in the WorkerHostBuilderExtensions class in the https://github.com/Azure/azure-functions-dotnet-worker project. There are a number of overloads, but they all end up calling this implementation:
public static IHostBuilder ConfigureFunctionsWorkerDefaults(this IHostBuilder builder, Action<HostBuilderContext, IFunctionsWorkerApplicationBuilder> configure, Action<WorkerOptions>? configureOptions)
{
builder
.ConfigureHostConfiguration(config =>
{
// Add AZURE_FUNCTIONS_ prefixed environment variables
config.AddEnvironmentVariables("AZURE_FUNCTIONS_");
})
.ConfigureAppConfiguration(configBuilder =>
{
configBuilder.AddEnvironmentVariables();
var cmdLine = Environment.GetCommandLineArgs();
RegisterCommandLine(configBuilder, cmdLine);
})
.ConfigureServices((context, services) =>
{
IFunctionsWorkerApplicationBuilder appBuilder = services.AddFunctionsWorkerDefaults(configureOptions);
// Call the provided configuration prior to adding default middleware
configure(context, appBuilder);
static bool ShouldSkipDefaultWorkerMiddleware(IDictionary<object, object> props)
{
return props is not null &&
props.TryGetValue(FunctionsApplicationBuilder.SkipDefaultWorkerMiddlewareKey, out var skipObj) &&
skipObj is true;
}
if (!ShouldSkipDefaultWorkerMiddleware(context.Properties))
{
// Add default middleware
appBuilder.UseDefaultWorkerMiddleware();
}
});
// Invoke any extension methods auto generated by functions worker sdk.
builder.InvokeAutoGeneratedConfigureMethods();
return builder;
}
ConfigureFunctionsWebApplication
ConfigureFunctionsWebApplication
is defined in the FunctionsHostBuilderExtensions class. Whiles the source code is in the same GitHub project as ConfigureFunctionsWorkerDefaults
, it ships as part of the Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore NuGet package.
It too contains a number of overloads, but they all end up calling this implementation:
public static IHostBuilder ConfigureFunctionsWebApplication(this IHostBuilder builder, Action<HostBuilderContext, IFunctionsWorkerApplicationBuilder> configureWorker)
{
builder.ConfigureFunctionsWorkerDefaults((hostBuilderContext, workerAppBuilder) =>
{
workerAppBuilder.UseAspNetCoreIntegration();
configureWorker?.Invoke(hostBuilderContext, workerAppBuilder);
});
builder.ConfigureAspNetCoreIntegration();
return builder;
}
internal static IHostBuilder ConfigureAspNetCoreIntegration(this IHostBuilder builder)
{
builder.ConfigureServices(services =>
{
services.AddSingleton<FunctionsEndpointDataSource>();
services.AddSingleton<ExtensionTrace>();
services.Configure<ForwardedHeadersOptions>(options =>
{
// By default, the X-Forwarded-For, X-Forwarded-Host, and X-Forwarded-Proto headers
// are sent by the host and will be processed by the worker.
options.ForwardedHeaders = ForwardedHeaders.All;
});
});
builder.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseUrls(HttpUriProvider.HttpUriString);
webBuilder.Configure(b =>
{
b.UseForwardedHeaders();
b.UseRouting();
b.UseMiddleware<WorkerRequestServicesMiddleware>();
b.UseEndpoints(endpoints =>
{
var dataSource = endpoints.ServiceProvider.GetRequiredService<FunctionsEndpointDataSource>();
endpoints.DataSources.Add(dataSource);
});
});
});
return builder;
}
So they're actually quite different!
Conclusion
I feel like the documentation suggesting using one or the other may be misleading. More likely you want to add a call to ConfigureFunctionsWebApplication
but leave the call to ConfigureFunctionsWorkerDefaults
(unless you really want to add in all your own calls to ConfigureHostConfiguration
and ConfigureAppConfiguration
)
var host = new HostBuilder()
.ConfigureFunctionsWebApplication()
.ConfigureFunctionsWorkerDefaults()
.Build();
Looks like I'm not alone with this either.
Categories: .NET, Azure Functions