如何将条件必需属性放入类属性中以使用 WEB API?

问题描述 投票:0回答:3

我只想添加条件必需属性,它与WEB API

一起使用

示例

public sealed class EmployeeModel
{
      [Required]
      public int CategoryId{ get; set; }
      public string Email{ get; set; } // If CategoryId == 1 then it is required
}

我通过(ActionFilterAttribute)使用模型状态验证

c# asp.net-mvc-4 asp.net-web-api custom-attributes
3个回答
54
投票

您可以实现自己的

ValidationAttribute
。也许是这样的:

public class RequireWhenCategoryAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var employee = (EmployeeModel) validationContext.ObjectInstance;
        if (employee.CategoryId == 1)
            return ValidationResult.Success;

        var emailStr = value as string;
        return string.IsNullOrWhiteSpace(emailStr)
            ? new ValidationResult("Value is required.")
            : ValidationResult.Success;
    }
}

public sealed class EmployeeModel
{
    [Required]
    public int CategoryId { get; set; }
    [RequireWhenCategory]
    public string Email { get; set; } // If CategoryId == 1 then it is required
}

这只是一个示例。它可能存在铸造问题,我不确定这是解决此问题的最佳方法。


7
投票

这是我的 2 美分。它会给你一个很好的消息,比如“当前的AssigneeType值Salesman需要AssigneeId”它也适用于枚举。

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class RequiredForAnyAttribute : ValidationAttribute
{
    /// <summary>
    /// Values of the <see cref="PropertyName"/> that will trigger the validation
    /// </summary>
    public string[] Values { get; set; }

    /// <summary>
    /// Independent property name
    /// </summary>
    public string PropertyName { get; set; }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var model = validationContext.ObjectInstance;
        if (model == null || Values == null)
        {
            return ValidationResult.Success;
        }

        var currentValue = model.GetType().GetProperty(PropertyName)?.GetValue(model, null)?.ToString();
        if (Values.Contains(currentValue) && value == null)
        {
            var propertyInfo = validationContext.ObjectType.GetProperty(validationContext.MemberName);
            return new ValidationResult($"{propertyInfo.Name} is required for the current {PropertyName} value {currentValue}");
        }
        return ValidationResult.Success;
    }
}

像这样使用它

public class SaveModel {
    [Required]
    public AssigneeType? AssigneeType { get; set; }

    [RequiredForAny(Values = new[] { nameof(AssigneeType.Salesman) }, PropertyName = nameof(AssigneeType))]
    public Guid? AssigneeId { get; set; }
}

0
投票

需要创建一个将进行验证的自定义验证类

 /// <summary>
 /// Provides conditional validation based on related 
     property value.
 /// </summary>
 [AttributeUsage(AttributeTargets.Property, 
   AllowMultiple = true)]
 public class RequiredIfAttribute : ValidationAttribute, IRequired
{
 #region Properties

/// <summary>
/// Gets or sets a value indicating whether other property's value should match or differ from provided other property's value (default is <c>false</c>).
/// </summary>
/// <value>
///   <c>true</c> if other property's value validation should be inverted; otherwise, <c>false</c>.
/// </value>
/// <remarks>
/// How this works
/// - true: validated property is required when other property doesn't equal provided value
/// - false: validated property is required when other property matches provided value
/// </remarks>
public bool IsInverted { get; set; }

/// <summary>
/// Gets or sets the other property name that will be used during validation.
/// </summary>
/// <value>
/// The other property name.
/// </value>
public string OtherProperty { get; private set; }

/// <summary>
/// Gets or sets the display name of the other property.
/// </summary>
/// <value>
/// The display name of the other property.
/// </value>
public string? OtherPropertyDisplayName { get; set; }

/// <summary>
/// Gets or sets the other property value that will be relevant for validation.
/// </summary>
/// <value>
/// The other property value.
/// </value>
public object? OtherPropertyValue { get; private set; }

/// <summary>
/// Gets or sets other properties that should be observed for change during validation
/// </summary>
/// <value>
/// Other properties separated by commas (CSV)
/// </value>
public string? PingPropertiesOnChange { get; set; }

/// <summary>
/// Gets a value that indicates whether the attribute requires validation context.
/// </summary>
/// <returns><c>true</c> if the attribute requires validation context; otherwise, <c>false</c>.</returns>
public override bool RequiresValidationContext => true;

#endregion Properties

#region Constructor

/// <summary>
/// Initializes a new instance of the <see cref="RequiredIfAttribute"/> class.
/// </summary>
/// <param name="otherProperty">The other property.</param>
/// <param name="equalsValue">Equals this value.</param>
public RequiredIfAttribute(string otherProperty, object? equalsValue) : base("'{0}' is required because '{1}' has a value {3}'{2}'.")
{
    OtherProperty = otherProperty;
    OtherPropertyValue = equalsValue;
    IsInverted = false;
}

#endregion Constructor

/// <summary>
/// Applies formatting to an error message, based on the data field where the error occurred.
/// </summary>
/// <param name="name">The name to include in the formatted message.</param>
/// <returns>
/// An instance of the formatted error message.
/// </returns>
public override string FormatErrorMessage(string name)
{
    return string.Format(
        CultureInfo.CurrentCulture,
        ErrorMessageString,
        name,
        OtherPropertyDisplayName ?? OtherProperty,
        OtherPropertyValue,
        IsInverted ? "other than " : "of ");
}

public virtual bool IsRequired(object? target)
{
    var otherProperty = target?.GetType()?.GetProperty(OtherProperty);
    if (otherProperty == null)
        return false;
    object? otherValue = otherProperty?.GetValue(target);
    // check if this value is actually required and validate it
    return (!IsInverted && Equals(otherValue, OtherPropertyValue)) || (IsInverted && !Equals(otherValue, OtherPropertyValue));
}

/// <summary>
/// Validates the specified value with respect to the current validation attribute.
/// </summary>
/// <param name="value">The value to validate.</param>
/// <param name="validationContext">The context information about the validation operation.</param>
/// <returns>
/// An instance of the <see cref="T:System.ComponentModel.DataAnnotations.ValidationResult" /> class.
/// </returns>
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
    ArgumentNullException.ThrowIfNull(validationContext);

    var otherProperty = validationContext.ObjectType.GetProperty(OtherProperty);
    if (otherProperty == null)
        return new ValidationResult($"Validation: Could not find a property named '{OtherProperty}'.");

    object? otherValue = otherProperty.GetValue(validationContext.ObjectInstance);
    if (otherValue is null && OtherPropertyValue is null)
        return ValidationResult.Success;
    if ((otherValue is null && OtherPropertyValue is not null))
        return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));

    // check if this value is actually required and validate it
    if ((!IsInverted && Equals(otherValue, OtherPropertyValue)) || (IsInverted && !Equals(otherValue, OtherPropertyValue)))
    {
        if (value == null)
            return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));

        // additional check for strings so they're not empty
        if (value is string val && string.IsNullOrWhiteSpace(val))
            return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
    }

    return ValidationResult.Success;
}
}

 // It can be used as follows:
        public sealed class TfaSmsOption
    {
        [RequiredIf(nameof(IsEnabled), true, ErrorMessage = "Phone number is required")]
        [PhoneNumber(ErrorMessage = "Phone number is invalid")]
        [StringLength(20, ErrorMessage = "Phone number must be less than 20 digits")]
        public string PhoneNumber { get; set; } = default!;
        
        public bool IsEnabled { get; set; }
    }
© www.soinside.com 2019 - 2024. All rights reserved.