API 컨트롤러 DTO에 사용할 사용자 지정 유효성 검사 속성을 만들었습니다. 이 속성에는 구성된 옵션의 값이 필요하기 때문에 생성자에이 값을 삽입하여 나중에 IsValid
and FormatErrorMessage
메서드 에서 옵션 서비스를 사용할 수 있습니다 .
internal class MyValidationAttribute : ValidationAttribute
{
private readonly IOptionsMonitor<MyOptions> myOptionsMonitor;
public MyValidationAttribute(IOptionsMonitor<MyOptions> myOptionsMonitor)
{
this.myOptionsMonitor = myOptionsMonitor;
}
public override bool IsValid(object value)
{
// ... use myOptionsMonitor here ...
return false;
}
public override string FormatErrorMessage(string name)
{
// ... use myOptionsMonitor here ...
return string.Empty;
}
}
불행히도 이것을 DTO의 속성으로 사용하고 싶을 때
internal class MyDTO
{
[MyValidationAttribute]
public string Foo { get; set; }
}
오류 메시지가 나타납니다.
'MyValidationAttribute.MyValidationAttribute (IOptionsMonitor)'의 필수 형식 매개 변수 'myOptionsMonitor'에 해당하는 인수가 없습니다.
유효성 검사 속성에 종속성 주입을 사용할 수있는 방법이 있습니까? 나는 내가 ValidationContext
그렇게 사용할 수 있다는 것을 안다.
internal class MyValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null)
{
IOptionsMonitor<MyOptions> myOptionsMonitor = validationContext.GetService<IOptionsMonitor<MyOptions>>();
// ...
return ValidationResult.Success;
}
return new ValidationResult("Something failed");
}
}
하지만 FormatErrorMessage
기본 클래스 의 메서드 를 사용하고 싶지만 옵션 서비스에 액세스 할 수 없습니다.
내 현재 솔루션
지금은 이것이 제가 사용하고있는 코드입니다.
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
internal class CustomValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
IOptionsMonitor<MyOptions> myOptionsMonitor = validationContext.GetService<IOptionsMonitor<MyOptions>>();
Dictionary<string, string> myMap = myOptionsMonitor.CurrentValue.MyMap;
string key = value.ToString() ?? string.Empty;
if (myMap.ContainsKey(key))
return ValidationResult.Success;
string[] formattedKeys = myMap.Keys.Select(key => $"'{key}'").ToArray();
string keysText = string.Join(" / ", formattedKeys);
string errorMessage = $"Invalid value. Valid ones are {keysText}";
return new ValidationResult(errorMessage);
}
}
속성은 이러한 목적으로 설계되지 않았습니다. 하지만 대신 액션 필터를 사용할 수 있습니다.
속성을 가능한 한 간단하게 만들어 보겠습니다. 여기에는 유효성 검사 논리가 필요하지 않습니다.
[AttributeUsage(AttributeTargets.Property)]
public class CustomValidationAttribute : Attribute
{ }
예를 들어 주입 할 서비스를 만들었습니다.
public class SomeService
{
public bool IsValid(string str)
{
return str == "Valid";
}
}
그리고 우리가 검증 할 클래스
public class ClassToValidate
{
[CustomValidation]
public string ValidStr { get; set; } = "Valid";
[CustomValidation]
public string InvalidStr { get; set; } = "Invalid";
}
이제 마지막으로 속성의 유효성을 검사하는 작업 필터를 만들 수 있습니다. 아래 스 니펫에서는 컨트롤러 작업이 실행되기 직전에 코드를 실행하기 위해 ASP.NET Core 파이프 라인에 연결합니다. 여기서 나는 행동 인수를 얻고 CustomValidationAttribute
모든 속성 을 찾으려고 노력 합니다. 존재하는 경우 속성에서 값을 가져 와서 유형으로 캐스트 (간단히 호출 .ToString()
)하고 서비스에 전달합니다. 서비스에서 반환 된 값에 따라 실행을 계속하거나 ModelState
사전 에 오류를 추가합니다 .
public class CustomValidationActionFilter : ActionFilterAttribute
{
private readonly SomeService someService;
public CustomValidationActionFilter(SomeService someService)
{
this.someService = someService;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
var actionArguments = context.ActionArguments;
foreach (var actionArgument in actionArguments)
{
var propertiesWithAttributes = actionArgument.Value
.GetType()
.GetProperties()
.Where(x => x.GetCustomAttributes(true).Any(y => y.GetType() == typeof(CustomValidationAttribute)))
.ToList();
foreach (var property in propertiesWithAttributes)
{
var value = property.GetValue(actionArgument.Value).ToString();
if (someService.IsValid(value))
continue;
else
context.ModelState.AddModelError(property.Name, "ModelState is invalid!!!");
}
}
base.OnActionExecuting(context);
}
}
Startup.cs
! 의 파이프 라인에 필터를 추가하는 것을 잊지 마십시오 .
services.AddMvc(x =>
{
x.Filters.Add(typeof(CustomValidationActionFilter));
});
최신 정보:
속성 내에서 종속성 주입을 엄격하게 사용하려면 서비스 로케이터 안티 패턴을 사용할 수 있습니다. 이를 위해 DependencyResolver.Current
ASP.NET MVC에서 에뮬레이트해야합니다.
public class CustomValidationAttribute : ValidationAttribute
{
private IServiceProvider serviceProvider;
public CustomValidationAttribute()
{
serviceProvider = AppDependencyResolver.Current.GetService<IServiceProvider>();
}
public override bool IsValid(object value)
{
// scope is required for scoped services
using (var scope = serviceProvider.CreateScope())
{
var service = scope.ServiceProvider.GetService<SomeService>();
return base.IsValid(value);
}
}
}
public class AppDependencyResolver
{
private static AppDependencyResolver _resolver;
public static AppDependencyResolver Current
{
get
{
if (_resolver == null)
throw new Exception("AppDependencyResolver not initialized. You should initialize it in Startup class");
return _resolver;
}
}
public static void Init(IServiceProvider services)
{
_resolver = new AppDependencyResolver(services);
}
private readonly IServiceProvider _serviceProvider;
public object GetService(Type serviceType)
{
return _serviceProvider.GetService(serviceType);
}
public T GetService<T>()
{
return (T)_serviceProvider.GetService(typeof(T));
}
private AppDependencyResolver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
}
초기화되어야합니다. Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
AppDependencyResolver.Init(app.ApplicationServices);
// other code
}
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다