miso_soup3 Blog

主に ASP.NET 関連について書いています。

API と MVC でモデル検証の挙動が少し違う

モデル検証の時に入るキーが、WEB API と、MVC と違っていたのでメモです。

下のように、とあるモデルを引数としたアクションメソッドを定義し、
そのモデルに検証属性を付けます。

// アクションメソッド
[HttpPost]
public ActionResult Register(RegisterModel model)
{
 //....
}
//モデル
public class RegisterModel
{
 [Required]
 public string FirstName { get; set; }
 [Required]
 public string LastName { get; set; }
}

このアクションメソッドに対して、下のような空のリクエストを送ると、

firstName=&lastName=

ModelState には、”〜〜を入力して下さい”といったエラーメッセージと、キーが格納されます。

この時の、キーが WEB APIMVC では違っていました。

MVC では

キーには、FirstName と LastName が入っています。

WEB API では

が、WEB API では、キーである FirstName と LastName に
"model" というプレフィクスがついています。
(このプレフィックスは、アクションメソッドの引数名です。)

困ること

WEB API で検証に失敗した時は、こちらの記事 ASP.NET Web API でモデルバインド時検証をカスタム ActionFilter を使って実装する のように、
キーとエラーメッセージを HttpResponse にいれて返すことが多いかと思います。

が、 リクエストでは、"firstName"で送っているのに、
HttpResponse の中身のプロパティが ”model.FirstName” になっていると変な感じです。

対処方法

MVC の挙動と同じようにしたい場合、2つの方法が考えられます。

  • カスタムモデルバインダーを作成して、引数のプレフィックスが付かないようにする。
  • ModelState から HttpResponse を作成するときに、プレフィックスをなんとかして削除する。

前者は、ソースを読み解く必要がありますし、
後者は、実現は簡単ですが、全ての先頭のプレフィクスを削除する、という実装に問題がないか検証が必要です。

前者が理想的ですが、難しそうだったのでとりあえず後者でしのぎました・・・。
アプローチとしては、先程の記事にある ValidationFilterAttribute 属性の中で HttpResponseMessage を作成するところを変更します。

actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);

このメソッドは、ModelState から HttpResponseMessage を作成しており、
ModelState の他にも、Exception や HttpError、文字列などからも作成できるように
いくつかオーバーロードが存在します。
(場所は、System.Net.Http.HttpRequestMessageExtensions)

なので、ソースを参考にしてして ModelState ではなく HttpError から、 HttpResponseMessage を作成すれば実現できます。