Web APIにおけるModelStateを使用したインターフェースパラメータの検証

ModelStateについて

MVCでは、ModelStateを使用してフォームの検証を行うことが一般的です。しかし、Web APIではビューが存在しないため、検証エラーをどのように処理するかが課題となります。ここでは、ModelStateを使用してAPIのパラメータを検証し、エラーメッセージをクライアントに返す方法を紹介します。

ModelStateの構造

ModelStateはDictionary型で、キーはモデルのプロパティ名、値はそのプロパティに関連するエラー情報を持っています。具体的には、ModelStateDictionaryModelStateクラスが関与しています。

public class ModelState
{
    public ModelErrorCollection Errors { get; }
    public ValueProviderResult Value { get; set; }
}

public class ModelError
{
    public string ErrorMessage { get; private set; }
    public Exception Exception { get; private set; }
}

コード実装

ログインシナリオを例にとって、ログイン用のモデルを作成し、検証ルールを定義します。

public class LoginModel
{
    [Required(ErrorMessage = "電話番号を入力してください")]
    [RegularExpression(@"^1[3|4|5|7|8][0-9]\d{8}$", ErrorMessage = "電話番号の形式が正しくありません")]
    public string PhoneNumber { get; set; }

    [Required(ErrorMessage = "認証キーが無効です")]
    public string AuthKey { get; set; }

    [Required(ErrorMessage = "認証コードを入力してください")]
    public string AuthCode { get; set; }
}

次に、APIアクション内でModelStateの検証を行います。

if (!ModelState.IsValid)
{
    string error = string.Empty;
    foreach (var key in ModelState.Keys)
    {
        var state = ModelState[key];
        if (state.Errors.Any())
        {
            error = state.Errors.First().ErrorMessage;
            break;
        }
    }
    return new HttpResponseMessage(HttpStatusCode.BadRequest)
    {
        Content = new StringContent(JsonConvert.SerializeObject(new { Status = "Failed", Message = error }), Encoding.UTF8, "application/json")
    };
}

フィルターを使用したコードの再利用

各APIアクションで同じコードを書くのは非効率的です。そこで、MVCのFilter機能を利用します。ActionFilterAttributeを継承したカスタム属性を作成し、OnActionExecutingメソッドをオーバーライドします。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true)]
public class ModelValidationAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        var modelState = actionContext.ModelState;
        if (!modelState.IsValid)
        {
            string error = string.Empty;
            foreach (var key in modelState.Keys)
            {
                var state = modelState[key];
                if (state.Errors.Any())
                {
                    error = state.Errors.First().ErrorMessage;
                    break;
                }
            }
            var response = new HttpResponseMessage(HttpStatusCode.BadRequest)
            {
                Content = new StringContent(JsonConvert.SerializeObject(new { Status = "Failed", Message = error }), Encoding.UTF8, "application/json")
            };
            actionContext.Response = response;
        }
    }
}

この属性をAPIコントローラーまたは個別のアクションに適用することで、モデルの検証とエラーレスポンスの生成を自動化できます。

タグ: ASP.NET Web API ModelState C# MVC

6月10日 20:02 投稿