'Enum fluent validation to accept only string
Below is my Enum
public enum IdentifierType
{
Customer = 1,
Manager = 2,
Director = 3
}
Using fluent validation in .Net core, is it possible to do validation where passing 1/"1" or 2/"2" or 3/"3" in the request should return validation error?
Passing "Customer" or "Manager" etc should work fine.
I know in C# enum type is 'int' but any thoughts if this is doable in first place?
Setup in startup.cs: Validator is registered before my converter.
services.AddControllers()
.AddFluentValidation(configuration =>
{
configuration.RegisterValidatorsFromAssemblyContaining<Startup>();
})
.ConfigureApiBehaviorOptions(opt => { opt.SuppressModelStateInvalidFilter = true; })
.AddJsonOptions(serializerOption =>
{
serializerOption.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
serializerOption.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
serializerOption.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
});
Attached behaviour of API using postman
Solution 1:[1]
The MVC framework has to convert JSON to a class for FluentValidation to work.
However, you can configure MVC to validate it first.
To allow the built-in JSON serialiser in any ASP.NET MVC Core app to allow strings to be converted into an enum, you need to add the JsonStringEnumConverter converter in your app startup. This converter also has a parameter you can set to false to disallow integer values. For example:
services
.AddMvc()
.AddJsonOptions(opts =>
{
opts.JsonSerializerOptions.Converters.Add(
new JsonStringEnumConverter(allowIntegerValues: false));
})
Since it seems that '' won't prevent an int being passed in as a string, you can write your own converter. For example, something like this would work:
public class IdentifierTypeConverter : JsonConverter<IdentifierType>{
public override IdentifierType Read(ref Utf8JsonReader reader,
Type typeToConvert, JsonSerializerOptions options)
{
var value = reader.GetString();
if(value == null)
{
throw new Exception("No null values thanks!");
}
if(int.TryParse(value, out var _))
{
throw new Exception("No numbers thanks!");
}
return (IdentifierType)Enum.Parse(typeof(IdentifierType), value);
}
public override void Write(Utf8JsonWriter writer, IdentifierType value,
JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}
You could make a generic version using a JsonConverterFactory. First make the converter generic:
public class EnumConverter<TEnum> : JsonConverter<TEnum> where TEnum : Enum
{
public override bool CanConvert(Type typeToConvert) => typeToConvert.IsEnum;
public override TEnum Read(ref Utf8JsonReader reader,
Type typeToConvert, JsonSerializerOptions options)
{
var value = reader.GetString();
if (value == null)
{
throw new Exception("No null values thanks!");
}
if (int.TryParse(value, out var _))
{
throw new Exception("No numbers thanks!");
}
return (TEnum)Enum.Parse(typeof(TEnum), value);
}
public override void Write(Utf8JsonWriter writer, TEnum value,
JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}
And make your factory:
public class EnumConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert) => typeToConvert.IsEnum;
public override JsonConverter? CreateConverter(Type typeToConvert,
JsonSerializerOptions options)
{
JsonConverter converter = (JsonConverter)Activator.CreateInstance(
typeof(EnumConverter<>).MakeGenericType(typeToConvert))!;
return converter;
}
}
And now add the factory instead:
opts.JsonSerializerOptions.Converters.Add(new EnumConverterFactory());
Solution 2:[2]
AFAIK there is no direct solution in fluent validations to solve your problem. However, I would handle this scenario something as stated below:
In the below code, I have created two properties - The IdentifierType field is public and it will accept a string value from your client. The other field is an enum and it can be used internally inside your project.
public class YourRequestModel
{
internal IdentifierType IdType
{
get
{
return Enum.Parse<IdentifierType>(IdentifierType);
}
}
public string IdentifierType { get; }
}
While doing fluent validation, validate the input only against the string value.
public class YourRequestModelValidator: AbstractValidator<YourRequestModel>
{
RuleFor(x => x.IdentifierType)
.IsEnumName(typeof(IdentifierType), caseSensitive: false);
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | codeninja.sj |


