Does anyone implemented a JsonFileSettings?

In order to read the settings from the application we normally point to the lovely documentation, but that only deals with XML and plain Text files…

Does anyone ever needed to extract the application settings from a json file like the appsettings.json?

using the current support as

var appSettings = 
   new ServiceStack.Configuration.TextFileSettings(
       "../appsettings.json".MapHostAbsolutePath());

I only get the first char { where my appsettings.json file is

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Verbose",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "AppSettings": {
    "Cache": {
      "TimeSpan": 5
    },
    "Algolia": {
      "AppId": "XXXXX",
      "ApiKey": "xxxxxxx",
      "HitsPerPage": 50
    },
    "Credentials": {
      "BaseUri": "http://api.xxxxxxx.dk",
      "Username": "api",
      "Password": "xxxxxxxx",
      "ImageBaseUri": "https://dynassets.xxx.dk"
    },
    "Smtp": {
      "Host": "127.0.0.1",
      "Port": 25,
      "Username": "",
      "Password":  ""  
    },
    "UrlRedirect": {
      "MainSite": {
        "Host": "localhost",
        "BaseUrl": "http://localhost:26892/"
      },
      "PortalSite": {
        "Host": "localhost",
        "BaseUrl": "http://localhost:26892/"
      },
      "AlternativeMainSite": {
        "Host": "gogift.demo.kodexe.se",
        "BaseUrl": "http://demo.goxxxx.sf2/"
      },
      "UnsupportedHostUrl": "http://localhost:26892/?error=unknowndomain"
    }
  }
}

would be nice to simple do:

var appSettings = new JsonFileSettings("../appsettings.json".MapHostAbsolutePath());
_appId = appSettings.Get<string>("Algolia.AppId");
_apiKey = appSettings.Get<string>("Algolia.ApiKey");
_hitsPerPage = appSettings.Get<int>("Algolia.HitsPerPage");

Thanks.

P.S. I know we can use Microsoft.AspNet.Builder.IApplicationBuilder to extract and use the applications, but I really wanted to stick with SStack.

We don’t have a JSON-based appSettings so you’ll need to add a Feature Request for it, but JSON isn’t a good format for configuration as it’s not human-friendly to read or write, lacks comments, etc.

Note: TextFileSettings is only for configuring simple flat settings, e.g:

Logging {IncludeScopes:false,Level:{Default:Verbose,System:Info,Microsoft:Info}}
Cache {TimeSpan:5}
Algolia {AppId:X,ApiKey:x,HitsPerPage:50}

But I’d recommend your Configuration and AppSettings be flat as possible. .NET for the longest time went with large XML config monstrosities, but have started to come back around to use simple config settings that most popular fx’s have settled on since they’re easier to read/write/use/copy/paste/etc.

Especially if you’re going to use dot notation to access it, it should really also be maintained in the same structure, e.g:

Algolia.AppId XXXX
Algolia.ApiKey xxxx
Algolia.HitsPerPage 5

It’s also easier to extend as you don’t need modify complex types to add new AppSettings and use appSettings.Get() directly.

We’re using appsettings.json on a ASP.NET Core project where we use Riot.js and bits of MVC… it was to be able to reuse that settings file…

we use it when building the website (on Startup.cs) as:


        private Task<bool> RedirectOldUrls(HttpContext context, IApplicationBuilder app)
        {
            var settings = app.ApplicationServices.GetService<Microsoft.Extensions.OptionsModel.IOptions<AppSettings>>();

            var mainSiteBaseUrl = settings.Value.UrlRedirect.MainSite.BaseUrl;
            var portalSiteBaseUrl = settings.Value.UrlRedirect.PortalSite.BaseUrl;
            var mainSiteHost = settings.Value.UrlRedirect.MainSite.Host;
            var portalSiteHost = settings.Value.UrlRedirect.PortalSite.Host;
            var unsupportedHostUrl = settings.Value.UrlRedirect.UnsupportedHostUrl;

            var alternativeMainSite = settings.Value.UrlRedirect.AlternativeMainSite;
           ...
       }

I just created a Feature Request. Maintaing configurations in different files and formats is a nightmare.
It is fine to have options, but please also support what MS considers as the default format.

MS maintains their own different config provider implementations, if you want to use their implementations register it in .NET Core’s IServiceCollection IOC and if you want to access it in AppHost.Configure() you can resolve it from ServiceStack’s IOC like you would any other dependency.

Otherwise if you prefer to use their IOptions<MyConfig> interfaces in your classes directly you can declare it as a public property and have it injected like a normal IOC dependency.

MS’s IConfigurationRoot and IOptions<T> typed config API’s doesn’t look compatible with IAppSettings Simple key/value API so I don’t see how an adapter would work with their complex type configs.

I was a little hesitant to use the SS appsettings over the aspnet core ones, but I think I have a pretty good solution now and have found the TextFileSettings pretty nice. This may or may not help you but maybe it will others. First, in the startup class add IHostingEnvironment to the constructor:

   public class Startup
    public IHostingEnvironment Env { get; set; }
    public Startup(IHostingEnvironment env)
        {
            Env = env;
        }

When you configure your apphost you can pass it in:

 _appHost = new AppHost(Env);

Then I do something like this using the TextFileSettings:

List<IAppSettings> settings = new List<IAppSettings>();
settings.Add(new EnvironmentVariableSettings());
if (env != null)
 {
    if (File.Exists("app.settings.{env.EnvironmentName}.txt".MapProjectPlatformPath()))
    {
       settings.Add(
        new TextFileSettings("app.settings.{env.EnvironmentName}.txt".MapProjectPlatformPath()));
     }
 }
 if (File.Exists("app.settings.txt".MapProjectPlatformPath()))
 {
    settings.Add(new TextFileSettings("app.settings.txt".MapProjectPlatformPath()));
  }
// could add some db ones as well
AppSettings = new MultiAppSettings(settings.ToArray());

I’ve found it really nice to be able to use the text files, database, and/or environment variables with one interface.

1 Like

What I like at the MS implementation is the value merging: Define defaults at the appsettings.json and override them within the appssetings.{environmen}.json.
What I would like to see is that SS is able to access these values for the configuration of the various plugins. Right now I have only a ugly solution: Reading the settings from the appsettings.json and provide them to SS via DictionarySettings:

AppSettings = new MultiAppSettings(new EnvironmentVariableSettings(),
new AppSettings(),
new DictionarySettings(new Dictionary<string, string>
{
{ “jwt.AuthKeyBase64” , AppConfigurationReader.JwtAuthKeyBase64 },
{ “jwt.RequireSecureConnection”, AppConfigurationReader.JwtRequireSecureConnection.ToString() },
{ “jwt.ExpireRefreshTokensIn” , AppConfigurationReader.JwtExpireRefreshTokensIn.ToString() },
{ “jwt.ExpireTokensIn” , AppConfigurationReader.JwtExpireTokensIn.ToString() },
})
);

I would appreciate a way to avoid that.

That’s exactly what the code I posted does although it seems the forum has issues with the pasting. It checks the following in order:

Environment Variables (azure)
appsettings.txt
appsettings.development.txt or appsettings.production.txt

(You could easily add app settings to your database as well)

I don’t recall if you can do this with the aspnet ones but it is nice:

 container.Register<CaptchaConfig>(AppSettings.Get<CaptchaConfig>("captchaConfig"));

 //meanwhile in the settings:
 captchaConfig {siteKey:thesitekey,secret:thesecret,url:https://www.google.com/recaptcha/api/siteverify}

Seems like a workable solution, it’s explicit, debuggable and clear exactly how it works. Not sure what magic impl is going to be able to intuitively work by mapping to their typed config property access to arbitrary string config keys.

Although I’d just avoid the DictionarySettings indirection in this case and just configure the Plugins directly, e.g:

new JwtAuthProvider {
    AuthKeyBase64 = AppConfigurationReader.JwtAuthKeyBase64,
}

Upon further review I was able to create an IAppSettings adapter for .NET Core’s new IConfiguration config model with the new NetCoreAppSettings adapter which you can use by assigning .NET Core’s pre-configured IConfiguration that’s injected into the Startup constructor when you register your AppHost, e.g:

public class Startup
{
    public IConfiguration Configuration { get; }
    public Startup(IConfiguration configuration) => Configuration = configuration;

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseServiceStack(new AppHost
        {
            AppSettings = new NetCoreAppSettings(Configuration)
        });
    }
}

This will let you use appsettings.json and .NET Core’s other Configuration Sources in ServiceStack under the IAppSettings API which works as you’d expect where you can get both primitive values and complex Types with Get<T>, e.g:

bool debug = AppSettings.Get<bool>("DebugMode", false);
MyConfig myConfig = AppSettings.Get<MyConfig>();

We’ve switched all our .NET Core 2.0 SPA projects to use the above .NET Core configuration model, e.g. here’s the react-spa source code.

Um, this is awesome. I was wondering why you were so against it. The app.config stuff doesn’t seem to work well with .net core anymore (it goes against the grain, doesn’t work on docker, etc…). I had to revert my react-spa to appsettings.txt but will now try this out.

Thanks!

Because it looked like there was only a Typed IOptions<T> API available but after some research I found the untyped general purpose API that could be used instead.

Web.config is still used in .NET Core Web projects (e.g. needed to configure IIS) and still applies the web.config transforms when a .NET Core App is published as our previous SPA templates did.

Ahhh ok, that makes sense.

What baffled me was, before ServiceStack I never could get app/web.config to work with asp.net core anything.

Then I saw the react-spa used it and it worked! (on IIS). But as soon as I deployed to docker, AppSettings.GetString() always returned null. I never said anything because I remember you saying Linux isn’t supported (or maybe low priority support).

Either way, glad you dug further!

Mono may not be supported but ServiceStack .NET Core on Linux definitely is, e.g. all our .NET Core Live Demos and .NET Core 2.0 Web Apps are deployed to Linux, many using Docker to deploy to AWS ECS. If there’s an issue with the latest version on Linux we’d definitely like to know about it.

1 Like

You may want to check out what happens when you deploy to docker with Web.config (using SS 5.0). I never got AppSettings.GetString to return anything, and had to resort to appsettings.txt. I was using the React Template.

Is the web.config deployed with the App?

Most of our .NET Core Apps are deployed to Docker and I’ve just upgraded NetCoreApps/RestFiles to use ServiceStack v5 with the Metadata DebugTemplate enabled so we can inspect the AppSettings that were populated from Web.config:

It’s deployed to AWS ECS and uses the appSettings in web.config which you can inspect by checking out its http://restfiles.netcore.io/metadata/debug and replacing the template with:

RootDirectory: {{ config.RootDirectory }}
ExcludeDirectories: {{ config.ExcludeDirectories | join }}
TextFileExtensions: {{ config.TextFileExtensions | join }}

Which will display the AppSettings sourced from the web.config:

RootDirectory: wwwroot/files/
ExcludeDirectories: .svn,bin,Properties
TextFileExtensions: txt,sln,proj,cs,config,asax,css,htm,html,xml,js,md

Interesting, I did set it to copy local. I might need to check that it was actually being copied. Thanks for the information, this will be extremely helpful.