How to extract a list from appsettings.json in .net core
In .NetCore this is what I did:
Normal Setup:
In your appsettings.json create a configuration section for your custom definitions:
"IDP": [
{
"Server": "asdfsd",
"Authority": "asdfasd",
"Audience": "asdfadf"
},
{
"Server": "aaaaaa",
"Authority": "aaaaaa",
"Audience": "aaaa"
}
]
Create a class to model the objects:
public class IDP
{
public String Server { get; set; }
public String Authority { get; set; }
public String Audience { get; set; }
}
in your Startup -> ConfigureServices
services.Configure<List<IDP>>(Configuration.GetSection("IDP"));
Note: if you need to immediately access your list within your ConfigureServices method you can use...
var subSettings = Configuration.GetSection("IDP").Get<List<IDP>>();
Then in your controller something like this:
Public class AccountController: Controller
{
private readonly IOptions<List<IDP>> _IDPs;
public AccountController(IOptions<List<Defined>> IDPs)
{
_IDPs = IDPs;
}
...
}
just as an example I used it elsewhere in the above controller like this:
_IDPs.Value.ForEach(x => {
// do something with x
});
Edge Case
In the case that you need multiple configs but they can't be in an array and you have no idea how many sub-settings you will have at any one time. Use the following method.
appsettings.json
"IDP": {
"0": {
"Description": "idp01_test",
"IDPServer": "https://intapi.somedomain.com/testing/idp01/v1.0",
"IDPClient": "someapi",
"Format": "IDP"
},
"1": {
"Description": "idpb2c_test",
"IDPServer": "https://intapi.somedomain.com/testing/idpb2c",
"IDPClient": "api1",
"Format": "IDP"
},
"2": {
"Description": "MyApp",
"Instance": "https://sts.windows.net/",
"ClientId": "https://somedomain.com/12345678-5191-1111-bcdf-782d958de2b3",
"Domain": "somedomain.com",
"TenantId": "87654321-a10f-499f-9b5f-6de6ef439787",
"Format": "AzureAD"
}
}
Model
public class IDP
{
public String Description { get; set; }
public String IDPServer { get; set; }
public String IDPClient { get; set; }
public String Format { get; set; }
public String Instance { get; set; }
public String ClientId { get; set; }
public String Domain { get; set; }
public String TenantId { get; set; }
}
Create Extension for Expando Object
public static class ExpandObjectExtension
{
public static TObject ToObject<TObject>(this IDictionary<string, object> someSource, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public)
where TObject : class, new()
{
Contract.Requires(someSource != null);
TObject targetObject = new TObject();
Type targetObjectType = typeof(TObject);
// Go through all bound target object type properties...
foreach (PropertyInfo property in
targetObjectType.GetProperties(bindingFlags))
{
// ...and check that both the target type property name and its type matches
// its counterpart in the ExpandoObject
if (someSource.ContainsKey(property.Name)
&& property.PropertyType == someSource[property.Name].GetType())
{
property.SetValue(targetObject, someSource[property.Name]);
}
}
return targetObject;
}
}
ConfigureServices
var subSettings = Configuration.GetSection("IDP").Get<List<ExpandoObject>>();
var idx = 0;
foreach (var pair in subSettings)
{
IDP scheme = ((ExpandoObject)pair).ToObject<IDP>();
if (scheme.Format == "AzureAD")
{
// this is why I couldn't use an array, AddProtecedWebApi requires a path to a config section
var section = $"IDP:{idx.ToString()}";
services.AddProtectedWebApi(Configuration, section, scheme.Description);
// ... do more stuff
}
idx++;
}
Assuming your appsettings.json
looks like this:
{
"foo": {
"bar": [
"1",
"2",
"3"
]
}
}
You can extract the list items like so:
Configuration.GetSection("foo:bar").Get<List<string>>()
You can use the Configuration binder to get a strong type representation of the configuration sources.
This is an example from a test that I wrote before, hope it helps:
[Fact]
public void BindList()
{
var input = new Dictionary<string, string>
{
{"StringList:0", "val0"},
{"StringList:1", "val1"},
{"StringList:2", "val2"},
{"StringList:x", "valx"}
};
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddInMemoryCollection(input);
var config = configurationBuilder.Build();
var list = new List<string>();
config.GetSection("StringList").Bind(list);
Assert.Equal(4, list.Count);
Assert.Equal("val0", list[0]);
Assert.Equal("val1", list[1]);
Assert.Equal("val2", list[2]);
Assert.Equal("valx", list[3]);
}
The important part is the call to Bind
.
The test and more examples are on GitHub