jason-roberts / FeatureToggle

Simple, reliable feature toggles in .NET
http://dontcodetired.com/blog/?tag=/featuretoggle
Apache License 2.0
689 stars 111 forks source link

AppSettingsProvider doesn't appear to correctly load from appSettings.json? #145

Closed AdrianLThomas closed 7 years ago

AdrianLThomas commented 7 years ago

Hi,

I get this exception at runtime: System.IO.FileNotFoundException: The configuration file 'appSettings.json' was not found and is not optional. The physical path is '[removed]\src\api\bin\Debug\netcoreapp1.1\appSettings.json'.

If I manually place the appSettings file in that location, the exception changes to the following: FeatureToggle.ToggleConfigurationError: The key 'FeatureToggle.ValuesFeature' was not found in AppSettings

This is the appSettings.json file:

{
  "FeatureToggle":{
    "ValuesFeature": "false",
    "NavigationFeature": "true"
  }
}

If I write my own settings provider to read from the appSettings file then it works fine - but looking at the sample code and documentation shouldn't this work?

jason-roberts commented 7 years ago

@AdrianLThomas thanks for reporting this, I'll look into it.

jason-roberts commented 7 years ago

@AdrianLThomas Hi Adrian, when using in ASP.NET Core you need to explicitly set the IConfigurationRoot otherwise the default behavior (e.g. when running in console app) is to look in the bin directory.

Example ASP.NET Core config: https://github.com/jason-roberts/FeatureToggle/blob/master/src/Examples/AspDotNetCoreExample/Startup.cs#L31-L41

Then in controller us DI: https://github.com/jason-roberts/FeatureToggle/blob/master/src/Examples/AspDotNetCoreExample/Controllers/HomeController.cs#L17

This is because the default config location is calculated using AppContext.BaseDirectory https://github.com/jason-roberts/FeatureToggle/blob/master/src/FeatureToggle.Shared/Internal/AppSettingsProvider.cs#L38 - as I understand it this resolves to the bin folder, whereas in ASP.NET Core we want the root folder than contains the appsettings.json

AdrianLThomas commented 7 years ago

Hi Jason, Thanks for your reply, and apologies for the delay.

So I did try explicitly setting the AppSettingsProvider, but I still get the error I mentioned in the original post. I've created a branch here that reproduces the issue: https://github.com/AdrianLThomas/Angular-and-ASP.NET-Core-Feature-Toggling/commit/1dba93bf475bc91b50d52a3c2a8664420bbbf789

Here's the full error message:

$ dotnet run
Hosting environment: Production
Content root path: C:\Src\adrianlthomas.github.com\Angular-and-ASP.NET-Core-Feature-Toggling\src\api
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.
fail: Microsoft.AspNetCore.Server.Kestrel[13]
      Connection id "0HL5SH1KQC7RD": An unhandled exception was thrown by the application.
FeatureToggle.ToggleConfigurationError: The key 'FeatureToggle.ValuesFeature' was not found in AppSettings
   at FeatureToggle.Internal.AppSettingsProvider.ValidateKeyExists(String key)
   at FeatureToggle.Internal.AppSettingsProvider.EvaluateBooleanToggleValue(IFeatureToggle toggle)
   at api.Controllers.FeaturesController.<>c.<Get>b__2_0(IFeatureToggle x) in C:\Src\adrianlthomas.github.com\Angular-and-ASP.NET-Core-Feature-Toggling\src\api\Controllers\FeaturesController.cs:line
 31
   at System.Linq.Enumerable.SelectListIterator`2.MoveNext()
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector)
   at api.Controllers.FeaturesController.Get() in C:\Src\adrianlthomas.github.com\Angular-and-ASP.NET-Core-Feature-Toggling\src\api\Controllers\FeaturesController.cs:line 33
   at lambda_method(Closure , Object , Object[] )
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeActionMethodAsync>d__27.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeNextActionFilterAsync>d__25.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeNextResourceFilter>d__22.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ResourceExecutedContext context)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeAsync>d__20.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Builder.RouterMiddleware.<Invoke>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Hosting.Internal.RequestServicesContainerMiddleware.<Invoke>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame`1.<RequestProcessingAsync>d__2.MoveNext()
jason-roberts commented 7 years ago

Great - thanks @AdrianLThomas - will take a look...

jason-roberts commented 7 years ago

@AdrianLThomas the nested json config is as of rc2, I updated your branch to NuGet 4.0.0-rc2 and both the values and features APIs now work.

AdrianLThomas commented 7 years ago

@jason-roberts that's great! Thank you. I was using RC1 at the time, looks like the recent update has sorted this :-)