所以尽管数据注解看起来很简单,少写了一些代码,但是开发软件应该更加注重可维护性,要尽量遵循那些设计原则,适当使用设计模式,写单元测试和E2E测试,尽管这样会造成看起来多写了一些代码,但是考虑到软件的质量以及更重要的后期维护,实际上这样做是大大的节省了成本。综上原因,我推荐使用第三方库,FluentValidation:https://github.com/JeremySkinner/FluentValidation。
使用FluentValidation安装FluentValidation,可以通过Nuget,Package Manager Console 或者 .net cli:
直接安装这个就可以:
然后会自动安装依赖的库:
把那些ResourceModel的数据注解验证约束都去掉,把Controller里面自定义验证的代码也去掉,然后为每一个类添加一个验证器Validator:
首先是Country的,这个简单:
其中大括号里面的字符串是参数(占位符),{PropertyName}就是属性的名字如果使用了WithName()方法,那就是WithName里面设定的别名;{MaxLength}就是指设定的最大长度约束的值。有很多这种占位符,还是需要看官方文档。
下面看看City相关的验证,这里有个继承的关系,首先是把共有的验证提取出来作为父类:
这里使用泛型比较好。
然后CityUpdateResource:
由于父子关系,父类的构造函数先执行,然后执行CityUpdateResourceValidator的构造函数。
最后还要为ASP.NET Core配置FluentValidation,在Startup的ConfigureServices方法里:
首先使用扩展方法AddFluentValidation();然后为每一个Resource Model 配置验证器。如果你不想挨个添加配置验证器的话,可以使用:
来把某个Assembly里的验证器全部添加进来,但是我还是比较喜欢一个一个写,重构的时候有什么错误能立即发现,但是也容易忘记添加。
然后测试一下,效果和之前是一样的。
使用FluentValidation,做到了很好的分离,我个人感觉非常好,虽然多写了些代码,但是更灵活,也更易于维护。
PATCH的验证
PATCH与POST和PUT的验证稍微有一点不同,首先看一个例子,删除一个不存在的属性的值:
这个会导致返回500错误,这是不对的。
这时,可已使用patchDoc.ApplyTo的一个重载方法,它可以接受ModelState作为参数,所以patchDoc里面有任何验证错误都会在ModelState里面体现出来,(注意是PatchDoc的验证错误而不是CityUpdateResource):
然后重新测试:
我之前已经设定了CityUpdateResource的Description属性是必填的,那我再做一个PATCH测试,把该属性的值去掉(设为null):