How to avoid RequestBindingException on a Enum

I have a route that uses Fluent Validation to process the input, and one of those properties is an Enum set as

[ApiMember(Name = "Visibility", Description = "Activity visibility", DataType = "string")]
[ApiAllowableValues("Visibility", typeof(ActivityVisibilityType))]
public ActivityVisibilityType? Visibility { get; set; }

and I made a Custom Rule to check the Enum type:

public static class CustomValidation
{
    public static IRuleBuilderOptions<T, TProperty> IsInEnum<T, TProperty>(this IRuleBuilder<T, TProperty> ruleBuilder)
    {
        return ruleBuilder.SetValidator(new IsInEnumValidator<TProperty>());
    }
}

...

public class IsInEnumValidator<T> : PropertyValidator
{
    public IsInEnumValidator() : 
       base("Property {PropertyName} it not a valid enum value.", 
            "NotInEnum") { }

    protected override bool IsValid(
        PropertyValidatorContext context)
    {
        return typeof(T).IsEnum && 
               Enum.IsDefined(typeof(T), context.PropertyValue);
    }
}

...

public class PostActivityValidator : AbstractValidator<PostActivityInformation>
{
    public PostActivityValidator()
    {
        RuleSet(ApplyTo.Post, () =>
        {
            RuleFor(r => r.Visibility).IsInEnum();
        }
    }
}

problem is, that if I don’t add a value, the error is thrown correctly, but if I add something that is not a valid enum, the process flow does never reach the validation as the return is

{
  "responseStatus": {
    "errorCode": "RequestBindingException",
    "message": "Unable to bind request",
    "stackTrace": null,
    "errors": null
  }
}

I understand the error, but how could I return a better error upon an Enum validation? like reaching my custom validation so I can throw a more friendly error just like when I’m not passing a value:

{
  "responseStatus": {
    "errorCode": "NotInEnum",
    "message": "Property Visibility it not a valid enum value.",
    "stackTrace": null,
    "errors": [
      {
        "errorCode": "NotInEnum",
        "fieldName": "Visibility",
        "message": "Property Visibility it not a valid enum value."
      }
    ]
  }
}

Unfortunately the only way to get past Request DTO Deserialization issues is to register a Custom Request Binder to take over deserializing the Request DTO yourself.

Other than that you could have the Enum property as a string so the Request DTO can deserialize, then add validation on that.

thought about that, but then I need to Enum.Parse() all calls internally, or create a unpublished method that actually does that for me…

wait a minute, that’s easy!

[ApiMember(Name = "Visibility", Description = "Activity visibility", DataType = "string")]
[ApiAllowableValues("Visibility", typeof(ActivityVisibilityType))]
public string Visibility { get; set; }

[IgnoreDataMember]
public ActivityVisibilityType? VisibilityEnum
{
    get
    {
        if (string.IsNullOrWhiteSpace(this.Visibility) ||
            !Enum.IsDefined(typeof(ActivityVisibilityType), this.Visibility))
        {
            return null;
        }

        return (ActivityVisibilityType)Enum.Parse(typeof(ActivityVisibilityType), this.Visibility);
    }
}

and I changed the IsInEnum to actually pass the enum type!

Thanks!

1 Like