一种方法是在您的配置类中添加一个特征IValidatable<T>。然后,您可以使用数据注释来定义应该验证的内容和不验证的内容。
我将提供一个示例,说明如何在您的解决方案中添加一个在一般情况下需要注意的辅助项目。
这里有我们要验证的类:
Configs/JwtConfig.cs
using System.ComponentModel.DataAnnotations;
using SettingValidation.Traits;
namespace Configs
{
public class JwtConfig : IValidatable<JwtConfig>
{
[Required, StringLength(256, MinimumLength = 32)]
public string Key { get; set; }
[Required]
public string Issuer { get; set; } = string.Empty;
[Required]
public string Audience { get; set; } = "*";
[Range(1, 30)]
public int ExpireDays { get; set; } = 30;
}
}
这是添加验证功能的“特征接口”(在 c# 8 中,可以将其更改为具有默认方法的接口)
SettingValidation/Traits/IValidatable.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.Extensions.Logging;
namespace SettingValidation.Traits
{
public interface IValidatable
{
}
public interface IValidatable<T> : IValidatable
{
}
public static class IValidatableTrait
{
public static void Validate(this IValidatable @this, ILogger logger)
{
var validation = new List<ValidationResult>();
if (Validator.TryValidateObject(@this, new ValidationContext(@this), validation, validateAllProperties: true))
{
logger.LogInformation($"{@this} Correctly validated.");
}
else
{
logger.LogError($"{@this} Failed validation.{Environment.NewLine}{validation.Aggregate(new System.Text.StringBuilder(), (sb, vr) => sb.AppendLine(vr.ErrorMessage))}");
throw new ValidationException();
}
}
}
}
一旦你有了这个,你需要添加一个启动过滤器:
SettingValidation/Filters/SettingValidationStartupFilter.cs
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using SettingValidation.Traits;
namespace SettingValidation.Filters
{
public class SettingValidationStartupFilter
{
public SettingValidationStartupFilter(IEnumerable<IValidatable> validatables, ILogger<SettingValidationStartupFilter> logger)
{
foreach (var validatable in validatables)
{
validatable.Validate(logger);
}
}
}
}
添加扩展方法是惯例:
SettingValidation/Extensions/IServiceCollectionExtensions.cs
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using SettingValidation.Filters;
using SettingValidation.Traits;
namespace SettingValidation.Extensions
{
public static class IServiceCollectionExtensions
{
public static IServiceCollection UseConfigurationValidation(this IServiceCollection services)
{
services.AddSingleton<SettingValidationStartupFilter>();
using (var scope = services.BuildServiceProvider().CreateScope())
{
// Do not remove this call.
// ReSharper disable once UnusedVariable
var validatorFilter = scope.ServiceProvider.GetRequiredService<SettingValidationStartupFilter>();
}
return services;
}
//
// Summary:
// Registers a configuration instance which TOptions will bind against.
//
// Parameters:
// services:
// The Microsoft.Extensions.DependencyInjection.IServiceCollection to add the services
// to.
//
// config:
// The configuration being bound.
//
// Type parameters:
// TOptions:
// The type of options being configured.
//
// Returns:
// The Microsoft.Extensions.DependencyInjection.IServiceCollection so that additional
// calls can be chained.
public static IServiceCollection ConfigureAndValidate<T>(this IServiceCollection services, IConfiguration config)
where T : class, IValidatable<T>, new()
{
services.Configure<T>(config);
services.AddSingleton<IValidatable>(r => r.GetRequiredService<IOptions<T>>().Value);
return services;
}
}
}
最后启用启动过滤器的使用
Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
...
services.ConfigureAndValidate<JwtConfig>(Configuration.GetSection("Jwt"));
services.UseConfigurationValidation();
...
}
}
我记得这段代码来自我现在无法找到的互联网上的一些博客文章,也许它和你找到的一样,即使你不使用这个解决方案,尝试将你所做的重构到另一个项目中,因此它可以在您拥有的其他 ASP.NET Core 解决方案中重复使用。