質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

ASP.NET

ASP.NETは動的なWebサイトやWebアプリケーション、そしてWebサービスを構築出来るようにする為、Microsoftによって開発されたウェブアプリケーション開発フレームワークです。

ASP.NET MVC Framework

ASP.NET MVC Frameworkは、MVCパターンをベースとした、マイクロソフトのウェブアプリケーション開発用のフレームワークです。

Q&A

解決済

1回答

1404閲覧

カスタムモデルバインダで取得失敗したプロパティがどれなのかを通知したい

YamakawaJunichi

総合スコア630

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

ASP.NET

ASP.NETは動的なWebサイトやWebアプリケーション、そしてWebサービスを構築出来るようにする為、Microsoftによって開発されたウェブアプリケーション開発フレームワークです。

ASP.NET MVC Framework

ASP.NET MVC Frameworkは、MVCパターンをベースとした、マイクロソフトのウェブアプリケーション開発用のフレームワークです。

0グッド

1クリップ

投稿2019/08/28 07:07

ASP.NET core MVC でwebアプリを作っています。
カスタムモデルバインダでパラメータの取得失敗した場合、ModelBindingContextのResultにModelBindingResult.Failed()を入れていますが、これだけだとどのパラメータの取得に失敗したかがコントローラに伝わりません。
これを伝える方法はあるでしょうか?

モデルクラス

C#

1[ModelBinder(BinderType = typeof(MyModelBinder))] 2public class Model 3{ 4 [Key] 5 public int Id { get; set; } 6 7 public string Foo { get; set; } 8 public string Bar { get; set; } 9 public string Baz { get; set; } 10}

モデルバインダ

C#

1public class MyModelBinder: IModelBinder 2{ 3 public Task BindModelAsync(ModelBindingContext bindingContext) 4 { 5 if (bindingContext == null) 6 throw new ArgumentNullException(nameof(bindingContext)); 7 8 var id = int.Parse(bindingContext.ValueProvider.GetValue("id").FirstValue); 9 var foo = bindingContext.ValueProvider.GetValue("foo").FirstValue; 10 var bar = bindingContext.ValueProvider.GetValue("bar").FirstValue; 11 var baz = bindingContext.ValueProvider.GetValue("baz").FirstValue; 12 13 if (foo != null && bar != null && baz != null) 14 { 15 bindingContext.Result = ModelBindingResult.Success(new Model 16 { 17 Id = id; 18 Foo = foo; 19 Bar = bar; 20 Baz = baz; 21 }); 22 } 23 else 24 { 25 bindingContext.Result = ModelBindingResult.Failed(); 26 27 // ここでどのプロパティが取れなかったかをコントローラに通知したい 28 29 } 30 return Task.CompletedTask; 31 } 32}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/08/29 00:47 編集

そもそもの目的は何なのでしょう。 (1) ユーザー入力の検証。(必須入力 = 空白は不許可なので、その場合はその旨エラーメッセージを表示しユーザーに再入力を促す) (2) 何らかの事情でカスタムモデルバインダーを使わなければならない、かつ、データアノテーション属性による検証が出来ない。なので、ユーザー入力の検証はカスタムモデルバインダー内で行わざるを得ない。 (3) その他。(なんでしょう? 説明いただけませんか)
guest

回答1

0

ベストアンサー

質問者さんは質問に対する私のコメントに答えないまま去ってしまったようですが、このまま放置されて終わってしまうのも何ですので、目的は以下のどれかであろうと想像して、レスしておきます。

(1) ユーザー入力の検証。(必須入力 = 空白は不許可なので、その場合はその旨エラーメッセージを表示しユーザーに再入力を促す)

データアノテーション属性を利用する。

CORE でも .NET Framework と同様に RequiredAttribute などを利用でき、JavaScript / jQuery によるクライアント側での検証も有効になるようです。

詳しくは以下の記事を見てください。

Add validation to an ASP.NET Core MVC app
https://docs.microsoft.com/ja-jp/aspnet/core/tutorials/first-mvc-app/validation?view=aspnetcore-2.2

(2) 何らかの事情でカスタムモデルバインダーを使わなければならない、かつ、データアノテーション属性による検証が出来ない。なので、ユーザー入力の検証はカスタムモデルバインダー内で行わざるを得ない。

CORE ではなくて .NET Framework の MVC5 の例ですが、ModelBindingContext.ModelState プロパティ で ModelSateDictionary オブジェクトを取得できるので、AddModelError メソッドでエラー情報を追加する。

CORE で同じようにできるかどうか分かりませんが、以下の .NET Framework ベースの MVC アプリ記事の検証をモデルバインダーで行うとすると、

MVC モデルのバリデーションについて
https://teratail.com/questions/80391

以下のようになります。

モデル / カスタムバインダー

public class CustomModelBinderSample { public bool Flg { get; set; } public string A { get; set; } public string B { get; set; } public string C { get; set; } public string D { get; set; } } public class CustomModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { if (bindingContext == null) { return new ArgumentNullException("bindingContext"); } var model = new CustomModelBinderSample(); model.Flg = FromPostedData<bool>(bindingContext, "Flg"); model.A = FromPostedData<string>(bindingContext, "A"); model.B = FromPostedData<string>(bindingContext, "B"); model.C = FromPostedData<string>(bindingContext, "C"); model.D = FromPostedData<string>(bindingContext, "D"); if (model.Flg) { // 事前に Trim した方がよさそうだが省略 if (string.IsNullOrEmpty(model.A)) { bindingContext.ModelState.AddModelError("A", "Flg = 1 and TextBox A empty"); } if (string.IsNullOrEmpty(model.B)) { bindingContext.ModelState.AddModelError("B", "Flg = 1 and TextBox B empty"); } if (string.IsNullOrEmpty(model.C)) { bindingContext.ModelState.AddModelError("C", "Flg = 1 and TextBox C empty"); } if (string.IsNullOrEmpty(model.D)) { bindingContext.ModelState.AddModelError("D", "Flg = 1 and TextBox D empty"); } } else { if (string.IsNullOrEmpty(model.D)) { bindingContext.ModelState.AddModelError("D", "Flg = 0 and TextBox D empty"); } } return model; } // ヘルパーメソッド private static T FromPostedData<T>(ModelBindingContext context, string key) { var result = context.ValueProvider.GetValue(key); context.ModelState.SetModelValue(key, result); return (T)result.ConvertTo(typeof(T)); } }

Controler

public ActionResult CustomModelBinderCreate() { return View(); } [HttpPost] [ValidateAntiForgeryToken] public ActionResult CustomModelBinderCreate([ModelBinder(typeof(CustomModelBinder))]   CustomModelBinderSample sample) { if (!ModelState.IsValid) { return View(sample); } return RedirectToAction("Index", "Home"); }

View

@model Mvc5App.Models.CustomModelBinderSample @{ ViewBag.Title = "CustomModelBinderCreate"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h2>CustomModelBinderCreate</h2> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h4>CustomModelBinderSample</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) <div class="form-group"> @Html.LabelFor(model => model.Flg, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> <div class="checkbox"> @Html.EditorFor(model => model.Flg) @Html.ValidationMessageFor(model => model.Flg, "", new { @class = "text-danger" }) </div> </div> </div> <div class="form-group"> @Html.LabelFor(model => model.A, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.A, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.A, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.B, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.B, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.B, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.C, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.C, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.C, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.D, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.D, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.D, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Create" class="btn btn-default" /> </div> </div> </div> } <div> @Html.ActionLink("Back to List", "Index") </div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") }

実行結果

イメージ説明

(3) その他

エスパーではない自分には答えられません。

投稿2019/08/29 11:53

編集2019/08/29 12:01
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

YamakawaJunichi

2019/08/30 09:09

回答ありがとうございます。 参考にします。
退会済みユーザー

退会済みユーザー

2019/08/30 09:33

参考にしますだけでは何だか分かりません。 お手数ですが、質問のコメントに書いた (1), (2), (3) に対してだけでもフィードバックください。
YamakawaJunichi

2019/08/30 09:57

(1)はカスタムモデルバインダの話ではないので回答として当を得ません。 (2)は回答として一番近いと思いますが、.NET coreではないとのことで参考にして.NET coreで同じことが出来るかの検討をします。 (3)は回答ですらありません。
退会済みユーザー

退会済みユーザー

2019/08/30 10:02

あなたの目的が何かと聞いているのですよ。質問からは (1) だとしか思えませんでしたけど? 一体何のですか?
退会済みユーザー

退会済みユーザー

2019/08/30 10:14

> (3)は回答ですらありません。 それは大変失礼なレスですよ。あなたが質問のコメント (1) ~ (3) にきちんと回答しなかったことはどう思っているのですか。(3) があるなら、それにあなたがきちんと回答したらどうですか。
YamakawaJunichi

2019/08/30 10:43

申し訳ないですが、質問の意が汲み取れませんでした。 「カスタムモデルバインダで取得失敗したプロパティがどれなのかを通知したい」という質問で、サンプルコードに判定をする箇所まで指定したにもかかわらず、目的は何かといわれても質問内容を読まれていないようにしか思えませんでした。
退会済みユーザー

退会済みユーザー

2019/08/30 10:45

話が通じないようですので撤退します。悪しからず。
YamakawaJunichi

2019/09/04 04:46

.NET coreでもModelBindingContext.ModelState.AddModelErrorメソッドが使用できたためベストアンサーとします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問