はじめに
ASP.NET Web APIのData Annotationsでリクエストモデルの値チェックをする場合、(万能ではないけれど)アクションフィルタを作ってしまった方が一々同じコードを書かなくて楽かも、という話
メソッド内に記述する
POSTメソッドのリクエストに対して、URLからIDを取り、ボディをデシリアライズしてモデルにバインドする場合を例にします。
Data Annotations以外にも、nullチェックを含めて、下記のような値チェックが必要になることが多いかと思います。
public class PackageController : ApiController
{
public async Task<HttpResponseMessage> Post([FromUri]string id, [FromBody]PackagingRequestModel request)
{
if (!string.IsNullOrWhiteSpace(id))
{
// ExceptionHandlerへ飛ばす。(ホントはidの存在チェックもある)
}
else if (request == null)
{
// Data Annotationsに引っかからないのでnullチェックも必要
// ExceptionHandlerへ飛ばす
}
else if (!ModelState.IsValid)
{
// ExceptionHandlerへ飛ばす
}
}
}
これを全てのインターフェイスで毎回書いてるのは面倒ですよね。
Actionフィルタを使う
前述の内容を次のようにフィルタ化してしまいます。
public class ValidationRequiredAttribute : ActionFilterAttribute
{
// パラメータのnullチェックをするかどうか
public bool IsNullable { get; set; }
public ValidationRequiredAttribute()
{
IsNullable = false;
}
public override async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
if (!IsNullable && actionContext.ActionArguments.Any(x=>x.Value == null))
{
// 引数がnull
}
else if (!actionContext.ModelState.IsValid)
{
// Data Annotationsでひっかかった
}
// その他チェックすべきものがあれば実装
await base.OnActionExecutingAsync(actionContext, cancellationToken);
}
}
IsNullableというプロパティは引数のnullチェックを行うかどうかを表していて、falseの場合は全てのパラメータが非nullである必要があります。
あとはこれをメソッドの上に付けてあげれば、基本的な値チェックはフィルタ内で済ませてからメソッドに入ってきてくれるので、ソースコードが大分すっきりします。
public class PackageController : ApiController
{
[ValidationRequired(IsNullable = false)]
public async Task<HttpResponseMessage> Post([FromUri]string id, [FromBody]PackagingRequestModel request)
{
// 独自の処理ロジックだけ書けばよい
}
}
以上。