Azure Functions logo

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.