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 API と MVC では違っていました。
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 を作成すれば実現できます。