相似的,有时候拼写的节点名称正确,但是属性名称可能拼写错误。例如, 我们将appSettings.json文件中的"WebhookUrl"错误的拼写为"Url"。这时我们调用前面例子中的TestController中的GET方法,会得到以下结果
{ "webhookUrl":null, "displayName":"My fancy bot", "shouldNotify":true }
强类型配置类的属性缺少SET访问器
我经常发现一些初级程序员会遇到这个问题,针对属性,他们只提供了GET访问器,而缺少SET访问器,在这种情况下强类型配置对象是不会正确绑定的。
public class SlackApiSettings { public string WebhookUrl { get; } public string DisplayName { get; } public bool ShouldNotify { get; } }
现在我们去调用前面例子中的TestController中的GET方法,会得到以下结果
{ "webhookUrl":null, "displayName":null, "shouldNotify":false }
不兼容的类型值
最后一种情况就是将一个不兼容的类型值,绑定到属性上。在配置文件中,所有的配置都是以文本形式保存的,但是绑定器需要将他们转换成.NET中支持的基础类型。例如ShouldNotify属性是一个布尔类型的值,我们只能将"True", "False"字符串绑定到这个值上,但是如果你在配置文件中,设置该属性的值为"THE VALUE", 当程序访问TestController时,程序就会报错
使用IStartupFilter创建一个配置验证
为了解决这个问题,我将使用IStartupFilter创建一个在应用启动时运行的简单验证步骤,以确保你的设置正确无误。
IStartupFilter接口允许你通过向依赖注入容器添加服务来间接控制中间件管道。 ASP.NET Core框架使用它来执行诸如“将IIS中间件添加到应用程序的中间件管道的开头, 或添加诊断中间件之类”的操作。
虽然IStartupFilter经常用来向管道中添加中间件,但是我们也可以不这么做。相反的,我们可以在程序启动时(服务配置完成之后,处理请求之前),使用它来执行一些简单的代码。
这里首先我们创建一个简单的接口,强类型配置类可以通过实现这个接口来完成一些必要的验证。
public interface IValidatable { void Validate(); }
下一步,我们创建一个SettingValidationStartupFilter类, 它实现了IStartupFilter接口
public class SettingValidationStartupFilter : IStartupFilter { readonly IEnumerable<IValidatable> _validatableObjects; public SettingValidationStartupFilter(IEnumerable<IValidatable> validatableObjects) { _validatableObjects = validatableObjects; } public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next) { foreach (var validatableObject in _validatableObjects) { validatableObject.Validate(); } return next; } }
在构造函数中,我们从依赖注入容器中取出了所有实现IValidatable接口的强类型配置对象,并在Configure方法中依次调用他们的Validate方法。
SettingValidationStartupFilter并没有修改任何中间件管道, Configure方法中直接返回了next对象。但是如果某个强类型配置类的验证失败,在程序启动时,就会抛出异常,从而阻止了程序。
接下来我们需要在Startup类中注册我们创建的服务SettingValidationStartupFilter
public void ConfigureServices(IServiceCollection services) { services.AddTransient<IStartupFilter, SettingValidationStartupFilter>() // 其他配置 }
最后你需要让你的配置类实现IValidatable接口, 我们以SlackApiSettings为例,这里我们需要验证WebhoolUrl和DisplayName属性是否绑定成功,并且我们还需要验证 WebhoolUrl是否是一个合法的Url。
public class SlackApiSettings : IValidatable { public string WebhookUrl { get; set; } public string DisplayName { get; set; } public bool ShouldNotify { get; set; } public void Validate() { if (string.IsNullOrEmpty(WebhookUrl)) { throw new Exception("SlackApiSettings.WebhookUrl must not be null or empty"); } if (string.IsNullOrEmpty(DisplayName)) { throw new Exception("SlackApiSettings.WebhookUrl must not be null or empty"); } // 如果不是合法的Url,就会抛出异常 var uri = new Uri(WebhookUrl); } }
当然我们还可以使用DataAnnotationsAttribute来实现上述验证。
public class SlackApiSettings : IValidatable { [Required, Url] public string WebhookUrl { get; set; } [Required] public string DisplayName { get; set; } public bool ShouldNotify { get; set; } public void Validate() { Validator.ValidateObject(this, new ValidationContext(this), validateAllProperties: true); } }
无论你使用哪一种方式,如果绑定出现问题,程序启动时都会抛出异常。
最后一步,我们需要将SlackApiSettings 以IValidatable接口的形式注册到依赖注入容器中,这里我们同样可以使用前文的方法解除对IOptions接口的依赖。