私たちのASP.net Core Web APIアプリケーションでは、ENUMが文字列として逆シリアル化されたときに、コントローラーメソッドがENUMプロパティを持つ複雑なオブジェクトを受け入れるときに、バインディングエラーをキャッチする方法を探しています。
例えば。
class Person
{
public string Name {get; set;}
public SexEnum Sex {get; set;}
}
enum SexEnum
{
Male,
Female
}
システム全体でStringEnumConverter
を使用しているため、Person
のJSONシリアル化インスタンスは次のようになります。
{
"name": "Ann",
"sex": "female"
}
次に、このJSONを投稿した場合(sex
プロパティのタイプミスに注意してください):
{
"name": "Ann",
"sex": "femal"
}
バインディングが失敗したため、コントローラーメソッドによって受信されたオブジェクト全体がNULLです。
そのバインディングエラーをキャッチし、何も問題がないかのようにパイプラインをコントローラーに送る代わりに、バインドに失敗したプロパティ値の詳細を含むBAD REQUESTをクライアントに返します。
逆シリアル化しようとしている型を知っています。逆シリアル化しようとしているプロパティの型を知っています。値が型に解析されないことがわかります。そのため、クライアントにその詳細を提供する方法が必要だと思います。これをどこにどのように接続するかわからない。
モデルのプロパティや列挙自体に属性を設定する必要なく、すべての列挙がカバーされるように、ソリューションをシステム全体に適用したいと考えています。 (これは、APIモデルを依存関係のないnugetパッケージとして配布するためです。)
上記のSimply Gedの回答AFAICSをフォローアップしています。モデルバインディングの例外が飲み込まれているため、これは実際には実行できません( https://github.com/aspnet/Mvc/issues/3898 )
ModelState
にはモデルバインディングエラーが含まれており、そこからいくつかの情報を取得できます。現在はJSONシリアル化のみを使用しているため、ModelState
のJsonSerializationException
エラーをチェックするフィルターを実装することになりました。それは例えば完璧ではありません。 JsonSerializationException
から(バインドに失敗した)要求された値を取得するには、内部例外メッセージを解析する必要があります。
誰かがより良い解決策を見つけたら、私は聞いてうれしいです。
最近この問題が発生し、それを処理するための独自の属性を記述しました。
public class ValidEnumValueAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
Type enumType = value.GetType();
bool valid = Enum.IsDefined(enumType, value);
if(!valid)
{
return new ValidationResult($"{value} is not a valid value for type {enumType.Name}");
}
return ValidationResult.Success;
}
}
class Person
{
public string Name {get; set;}
[ValidEnumValue]
public SexEnum Sex {get; set;}
}
次に、エラーがModelStateに追加されるため、ModelState.IsValid
値が有効かどうかを確認します。
if(!ModelState.IsValid)
{
return BadRequest(ModelState);
}
[〜#〜]編集[〜#〜]
属性を使用したくない場合は、NewtonSoft StringEnumConverter
から新しいコンバーターを派生させ、jsonを読み取る前に値が有効であることを確認することができます。
public class validEnumConverter : StringEnumConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if(!Enum.IsDefined(objectType, reader.Value))
{
throw new ArgumentException("Invalid enum value");
}
return base.ReadJson(reader, objectType, existingValue, serializer);
}
}
これは、スタートアップクラスのJsonOptionsに追加されます。
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.Converters.Add(new validEnumConverter());
});
}
@Simply Gedの優れた答えの2番目の部分を拡張すると、null可能列挙型を使用してSystem.ArgumentException: 'Type provided must be an Enum.'
例外。 null可能な列挙型、または列挙型のnull値を処理するには、追加の手順が必要です。
public class validEnumConverter : StringEnumConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
Type enumType = (Nullable.GetUnderlyingType(objectType) ?? objectType);
if(!Enum.IsDefined(enumType, reader.Value ?? string.Empty))
{
throw new ArgumentException("Invalid enum value");
}
return base.ReadJson(reader, objectType, existingValue, serializer);
}
}