I have a simple controller that reads some configuration from appsettings.json
using the Options pattern. It works fine as long as appsettings.json
is configured correctly. However, what if my configuration section is missing from appsettings.json
? I hoped to get an Exception
or null
, but instead I get a MySettings
object with default values (i.e. MyProperty
is null
).
MyController.cs
public class MyController : Controller
{
private readonly string value;
public MyController(IOptions<MySettings> options)
{
// Can I validate the `options` object here?
this.value = options.Value.MyProperty;
}
[HttpGet("api/data")]
public ActionResult<string> GetValue()
{
return this.Ok(this.value);
}
}
MySettings.cs
public class MySettings
{
public string MyProperty { get; set; }
}
Startup.cs (only showing relevant code)
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Tried this:
services.Configure<MySettings>(Configuration.GetSection("MySettings"));
// Tried this too:
services.Configure<MySettings>(options => Configuration.GetSection("MySettings").Bind(options));
}
}
appsettings.json
{
"MySettings": {
"MyProperty": "hello world"
}
}
CodePudding user response:
You can enforce it in the following way:
- Add data annotation to your model
public class MySettings
{
[Required]
public string MyProperty { get; set; }
}
- Call
ValidateDataAnnotations
afterBind
call
services
.Configure<MySettings>(options => Configuration
.GetSection("MySettings")
.Bind(options))
.ValidateDataAnnotations();
- Wrap the property access in a try catch where you except
OptionsValidationException
try
{
this.value = options.Value.MyProperty;
}
catch(OptionsValidationException ex)
{
//handle the validation exception
}
CodePudding user response:
I found one possible solution on this SO answer.
Add an extensions method to validate:
public static class ConfigurationExtensions
{
public static T GetValid<T>(this IConfiguration configuration)
{
var config = configuration.Get<T>();
try
{
Validator.ValidateObject(config, new ValidationContext(config), true);
}
catch(Exception e)
{
throw new NotSupportedException("Invalid Configuration", e);
}
return config;
}
}
Use that extension method to validate & register the instance in Startup.cs:
var settings = Configuration.GetSection("MySettings").GetValid<MySettings>();
services.AddSingleton(settings);
Change the controller constructor to accept the MySettings
object directly (instead of IOptions<MySettings>
):
public MyController(MySettings options)
{
this.value = options.MyProperty;
}
Another nice consequence of this approach is it removes our dependency on IOptions
which simplifies the controller a bit.