Geeks With Blogs

News This is the *old* blog. The new one is at blog.sixeyed.com
Elton Stoneman
This is the *old* blog. The new one is at blog.sixeyed.com

After Part 1 and Part 2, we now have an app config server with a nice UI and a publishing workflow for changes. In our app, we can get the current config settings with an HTTP client reading from the URL for the config document:

http://config.mydomain.com/dev/app1

And we’ll get a response like this:

{ 
   "connectionStrings": 
   { 
       "redis": "connection;string;goes;here;", 
       "sqlServer": "and;for;sql;" 
   }, 
   "caching": 
   { 
       "lifespan": "PT10M", 
       "maximumSize": 300 
   }, 
   "appSettings": 
   { 
       "termsAndConditionsUrl": "http://static.xyz.com/tandcs.html" 
   } 
}

That’s HTTP+JSON so we could use this for configuring any type of app. In this post I’ll walk through consuming the config in a .NET app, and highlight some of the concerns you need to think about when you have a remote config store. The full config client in C# is on github here: A gist for accessing remote JSON app config.

Static or Dynamic?

It’s easy to change the structure of the output from the config API just by changing the properties in the document type, or the response generated by the template. We can do that at runtime without a release. In the consumer, we can either write a class with the known config properties to statically model the structure, or we can use a dynamic object and just parse whatever JSON comes back.

The static option may seem sensible - you can’t use new config properties without changing your app anyway, but the flexibility of the dynamic object could useful for supporting change. It also means you need a lot less code if your config has nested elements and repeating arrays. So let’s see how that looks. I have a static Config class with a static dynamic property, Current:

public static dynamic Current
{
   get
   {
      EnsureConfig();
      return (JObject)_Cache["Current"];
   }
}

The dynamic property returns a JSON.NET JObject, so to use it and retrieve the configured Redis connection string, I just do:

var connectionString = (string)Config.Current.connectionStrings.redis

The downside of the dynamic approach is that I need to cast the property I’m accessing to the type I expect it to be, but the benefit is that is that the only code I write to model my app config is the template in CMS.

Loading and Caching Config Settings

You don’t want to make a Web call every time you check a config setting, so you’ll want to cache the response locally. The size of a settings object will be tiny, so it’s safe to do that in memory and the .NET MemoryCache is a fine option for that. The EnsureConfig method in my Config class populates the config object in the cache if it doesn’t exist:

private static MemoryCache _Cache = new MemoryCache("_Config");

private static void EnsureConfig()
{
   if (_Cache["Current"] == null)
   {
      using (var client = new WebClient())
      {
         client.Headers.Add("accept", "application/json");
         var json = client.DownloadString(_ConfigUrl);
         _Cache.Add("Current", JObject.Parse(json), new CacheItemPolicy { 
            AbsoluteExpiration = DateTimeOffset.Now.Add(_CacheLifespan)
         });
      }
   }
}

If there’s nothing in the cache then it’s either the first time the config has been used, or the old config settings in the cache have expired. So I fetch the settings from the config API - I’m using the old WebClient rather than the portable HttpClient because I don’t need this to run async – config will be fetched on-demand.

I’m using absolute expiration, so I know my config cache will only last for a certain time period. When I make a change in Umbraco and publish it, I know how long it will be before the app uses the new values. It also helps load on Umbraco and performance in your app. If you make hundreds of config checks, you don’t want to make a Web call for every one.

If you can cache for 30 seconds, then it means your app only makes one call every 30 seconds, when it’s in use (config is loaded on demand, so if the app is quiet it won’t make any calls) and you don’t need to scale Umbraco - its maximum load from the app is only 2 requests per second.

Configuring the Config

Where do you store the URL for config server? That would depend on your solution and how you set up your environments. Ideally you would do it through convention, so if your config is for an API that lives at api.xyz.com, then you can assume the config URL is at config.xyz.com/api/myapp. And if you have domains for non-production environments, dev.api.xyz.com and test.api.xyz.com, then you can modify your config URL to match the running environment.

If convention isn’t an option then you need to revert to the native config  management for your platform, so in this case we would use the .NET config manager to store the URL for the config API:

private static readonly string _ConfigUrl;
private static readonly TimeSpan _CacheLifespan;

static Config()
{
   _ConfigUrl = ConfigurationManager.AppSettings["ApiConfig.Url"];
   _CacheLifespan = XmlConvert.ToTimeSpan(ConfigurationManager.AppSettings["ApiConfig.CacheSeconds"]);
}

And then the App.config file just contains enough to load from the config API:

<appSettings>
   <add key="ApiConfig.Url" value="http://config.xyz.com/api/myapp"/>
   <add key="ApiConfig.CacheDuration" value="PT60S" />
</appSettings>

I’m also storing the cache duration for the config, but of course you could keep that as a setting in Umbraco too.

With our app config living in Umbraco and our app using a simple cache, we get a very nice config experience which is fast and easy to set up. And as we’ll see at the end of this series, it’s easy to configure access control to limit what different users can see and do.

But What About Security?

At this point we have a CMS which is secure for change, but an API which is publicly available. Potentially you’ll have connection strings, API keys or other sensitive information that you don’t want seen, so how do you secure your config API? That depends on the consumer. If the consumer is a server-side app then IPSec can be a simple option, so you whitelist the IP addresses of your servers, an no other machine can connect to read your config.

If the consumer is a mobile app then you’d need to do a bit more work as the API needs to be publicly available. HTTPS for starters, and you can make the document template smarter and encrypt config values, and then decrypt them in the client – provided you can keep keys securely. Or you could return plain text instead of JSON and encrypt the whole string.

Either way, you have the property values as plain text in the CMS, and use the Umbraco security model there, and the API would expose encrypted values that only trusted clients could decipher.

Posted on Friday, June 27, 2014 12:16 PM Umbraco , APIs | Back to top


Comments on this post: Using CMS for App Configuration - Part 3, Consume Your Config

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Elton Stoneman | Powered by: GeeksWithBlogs.net