Azure-functions-host: routePrefix ignored when using ConfigurationBuilder

Created on 30 Apr 2019  路  16Comments  路  Source: Azure/azure-functions-host

In WebJobsStartup, if merging original configuration with custom configuration, the routePrefix defined in host.json is ignored and all HttpTrigger functions use "api" as routePrefix

I have not deployed this to any Azure resource yet, and I'm only running locally, so please bear with the lack of investigative information

Investigative information

Please provide the following:

  • Timestamp: 2019-04-30
  • Function App version: 2.0
  • Function App name: N/A
  • Function name(s) (as appropriate): N/A
  • Invocation ID: N/A
  • Region: N/A

Repro steps

Use the following code in a WebJobsStartup to add support for custom config-files

[assembly: WebJobsStartup(typeof(Startup))]
namespace MyFuncApp {
    public class Startup : IWebJobsStartup {
        public void Configure(IWebJobsBuilder builder)
        {
            //Get the current config and merge it into a new ConfigurationBuilder to keep the old settings
            ConfigurationBuilder configBuilder = new ConfigurationBuilder();
            var descriptor = builder.Services.FirstOrDefault(d => d.ServiceType == typeof(IConfiguration));
            if (descriptor?.ImplementationInstance is IConfigurationRoot configuration)
            {
                configBuilder.AddConfiguration(configuration);
            }

            IConfigurationRoot config = configBuilder.Build();

            //replace the existing config with the new one
            builder.Services.Replace(ServiceDescriptor.Singleton(typeof(IConfiguration), config));
    }
}

This should use the existing configurations inside a new ConfigurationBuilder. I could now add .AddJsonFile(...) if I wanted to, however for this example I've just created a new ConfigurationBuilder, copied the existing config into this builder, created a new config object from this builder, and replace the old config object with the new one, so there are no custom configurations to override anything.

If I debug I can see that the config variable contains a (private) ChainedConfigurationProvider that contains all the default configurations, including the HostJsonFileConfigurationProvider that has routePrefix: "".

When starting the Function app locally I can see that the routePrefix is now ignored, and all my functions have "api" at the start of their route, even though my host.json has a blank value. Here's my host.json:

{
  "version": "2.0",
  "extensions": {
    "http": {
      "routePrefix": ""
    }
  }
}

It all boils down to this line of code:

builder.Services.Replace(ServiceDescriptor.Singleton(typeof(IConfiguration), config));

If I remove this the routePrefix is blank (as stated in the host.json file). If this line is used the routePrefix is "api".

It would therefore seem that there might be a bug in the routePrefix resolver that fails to read the prefix from host.json whenever a custom IConfiguration is used, even if this configuration should have merged in the settings from the old configuration.

Expected behavior

Configurations should be merged without overwriting routePrefix functionality

Actual behavior

routePrefix gets reset to "api" even if it is "" in host.json

Known workarounds

N/A

Related information

  • Obviously coded in C#
  • HttpBindings
2.x bug

Most helpful comment

What I seem to have observed in testing this out is that the value specified in host.json using the above approach is obeyed if you use a value other than "". Example, I set it to "foo" and my routes were discovered with a routePrefix of /foo. There seems to be something about empty string specifically.

All 16 comments

This just bit me yesterday for a couple hours. Some guidance in #4726 may help in determining what the correct way to do this is without losing this information.

So sorry for the huge delay here. I thought we had resolved this one.

@fabiocav / @brettsam had insights about this one.

@gthvidsten as a workaround you can do the following in the startup:

IServiceProvider provider = builder.Services.BuildServiceProvider();
IConfiguration existingConfig = provider.GetRequiredService<IConfiguration>();
var yourBuilder = new ConfigurationBuilder().AddConfiguration(existingConfiguration);//add your providers as needed
services.Replace(ServiceDescriptor.Singleton(typeof(IConfiguration), yourBuilder.Build()));

This works for functions runtime V3, tested both locally and in the cloud.

@gthvidsten Also, I started to see random OOM exceptions in my V3 funcs, I'm not sure whether it's connected to config replacement(I think not) but still wanted to mention a possible problem.

@Badabum do you have details on the OOM exception you ran into? Would you be able to share that in a different issue?

@fabiocav yup, I added a comment in another related issue.

I'm still seeing this bug / behaviour in functions v2 when attempting to build custom configuration. The routePrefix that I have set to an empty string in the host json file is being ignored and all routes are being prefixed with /api.

@joshua-hayes @fabiocav I see the same in v3(empty routePrefix) results in /api prefix.

Same here

Any news?

Woked around with .Configure<HttpOptions>(fun (options : HttpOptions) -> options.RoutePrefix <- String.Empty; ()) for now. But it is weird

What I seem to have observed in testing this out is that the value specified in host.json using the above approach is obeyed if you use a value other than "". Example, I set it to "foo" and my routes were discovered with a routePrefix of /foo. There seems to be something about empty string specifically.

The workaround mentioned by @xperiandri seemed to do the trick for now.
I added the the following line to to the Startup.cs:

services.Configure<HttpOptions>(options => options.RoutePrefix = string.Empty);

Confirmed what @andrewdmoreno says - works with any value but an empty string.

Assingning this to sprint 82 for validation using the new configuration APIs

I was able to reproduce the error - it appears that using Functions version 2.0.14248.0 (with the new API's expected), the issue persists. Filing as a bug.

Was this page helpful?
0 / 5 - 0 ratings