ASP.NET Core MVC: Erweitern Sie die Funktionsweise des RequiredAttribute
Posted: 30 Dec 2024, 19:06
Ich möchte die Funktionsweise des RequiredAttribute mit stark typisierten Ansichtsmodellen erweitern. Ich habe zum Beispiel ein Referenzdatenansichtsmodell, das so aussieht:
Dies kann auf anderen Ansichtsmodellen wie folgt verwendet werden:
Mit der RequiredRef sollte die Eigenschaft selbst nicht null sein (was vom RequiredAttribute behandelt wird), sondern auch die Id sollte auch nicht null sein.
Ich weiß, dass ich dies erreichen kann, indem ich mein eigenes ValidationAttribute erstelle (siehe unten), aber ich würde gerne das integrierte RequiredAttribute verwenden für Konsistenz.
Ich habe versucht, einen AttributeAdapter zu verwenden (der in ASP.NET MVC zu funktionieren scheint), aber das hilft nicht. Ich habe das Gefühl, dass mir etwas fehlt:
Dies wird beim Start registriert:
Code: Select all
public class ReferenceDataViewModel
{
public int? Id { get; set; }
public string? Name { get; set; }
}
Code: Select all
public class MyEditViewModel
{
// Optional: this property can be null or this property's Id value can be null
public ReferenceViewModel? OptionalRef { get; set; }
// Required: this property should not be null nor should this property's Id value
[Required]
public ReferenceViewModel? RequiredRef { get; set; }
}
Ich weiß, dass ich dies erreichen kann, indem ich mein eigenes ValidationAttribute erstelle (siehe unten), aber ich würde gerne das integrierte RequiredAttribute verwenden für Konsistenz.
Code: Select all
public class ExtendedRequiredAttribute : RequiredAttribute
{
public ExtendedRequiredAttribute() { }
public ExtendedRequiredAttribute(RequiredAttribute attributeToCopy)
{
AllowEmptyStrings = attributeToCopy.AllowEmptyStrings;
if (attributeToCopy.ErrorMessage != null)
{
ErrorMessage = attributeToCopy.ErrorMessage;
}
if (attributeToCopy.ErrorMessageResourceType != null)
{
ErrorMessageResourceName = attributeToCopy.ErrorMessageResourceName;
ErrorMessageResourceType = attributeToCopy.ErrorMessageResourceType;
}
}
public override bool IsValid(object? value)
{
if (value is ReferenceDataViewModel entityReference)
{
return entityReference?.Id != null;
}
return base.IsValid(value);
}
}
Code: Select all
public class ExtendedRequiredAttributeAdapter : AttributeAdapterBase
{
public ExtendedRequiredAttributeAdapter(ExtendedRequiredAttribute attribute, IStringLocalizer? stringLocalizer)
: base(attribute, stringLocalizer)
{ }
public override void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-required", GetErrorMessage(context));
}
public override string GetErrorMessage(ModelValidationContextBase validationContext)
{
ArgumentNullException.ThrowIfNull(validationContext);
return GetErrorMessage(validationContext.ModelMetadata, validationContext.ModelMetadata.GetDisplayName());
}
}
public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
{
private readonly IValidationAttributeAdapterProvider _innerProvider = new ValidationAttributeAdapterProvider();
public IAttributeAdapter? GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer? stringLocalizer)
{
var type = attribute.GetType();
if (type == typeof(RequiredAttribute))
{
var requiredAttribute = (RequiredAttribute)attribute;
var extendedRequiredAttrib = new ExtendedRequiredAttribute(requiredAttribute);
return new ExtendedRequiredAttributeAdapter(extendedRequiredAttrib, stringLocalizer);
}
return _innerProvider.GetAttributeAdapter(attribute, stringLocalizer);
}
}
Code: Select all
services.AddSingleton();