【ASP.NET MVC】更新アクションの作り方について

このQ&Aのポイント
  • ASP.NET MVCのコントローラで、フォーム送信を受けて動作する更新アクションの基本的な作り方について説明します。
  • 更新アクションでは、入力内容の検証やデータベースの更新を行います。フォーム値の検証や複雑な検証の追加方法、見通しの良いコードの書き方についても解説します。
  • その他、TempDataを使用してエラーやメッセージを表示する方法や、更新処理を別のメソッドに分割する方法についても紹介します。
回答を見る
  • ベストアンサー

【ASP.NET MVC】更新アクション

いつもお世話になっております。 ASP.NET MVCのコントローラで、フォーム送信を受けて動作する更新アクションの作り方について質問させてください。 いろいろな記事やチュートリアルを読んで、なんとか次のような基本計については理解できました。 public class HomeController { [HttpPost] public ActionResult UpdateItem(Item item){  if (! ModelState.IsValid){   TempData["error"] = "入力内容にエラーがあります";   return View("ItemEdit", item);  } else {   var result = false;   //   // 延々と更新メソッドが続く   //   if (result) {     TempData["message"] = "正しく更新できました"     ModelState.Clear();     return Redirect("ItemList");   } else {     TempData["error"] = "更新でエラーが起きました";     return View("ItemEdit", item);   }  } } } TempData["xxx"]の処理は、ビュー側で、値に応じてjQueryUIによりダイアログを表示します。 質問したい箇所は以下の3つです。 ■一体、全体の流れはこれでいいのでしょうか? if文とTempDataばっかりで。。何かスッキリしません。 ■フォーム値の検証はアクション呼び出しの時点で終わっていますが、ここに任意の検証を入れたい場合はどうしたらいいのでしょう? (必須や文字種チェックはモデルで属性設定を行っていますが、それ以外の例えば2項目が連動するような検証) ■サンプルの例文は簡単ですが、DB更新などが伴うと数百行になるため、すごく見通しが悪いです。全部別メソッドにするのも。。いい方法はないでしょうか 不明点だらけですんません(笑 どなたか愛の手を

質問者が選んだベストアンサー

  • ベストアンサー
  • shockatz
  • ベストアンサー率80% (153/191)
回答No.2

こんにちは ■全体の流れ 検証NG、検証OK->更新OK、検証OK->更新NGと、きちんと処理が書かれているので、よくまとまっていると思います。 onosさんはViewBagでメッセージを返すように仰っていますがRedirectのように画面が遷移して、遷移後にメッセージを表示しなければいけない場合には、TempDataのほうが便利です(nayutaxさんのコードで気づきました^^; これ賢いやり方ですね) ■フォーム値の検証 モデルバインディングまで踏込むと大事になるので(笑、ここはカスタム検証を定義すればいいと思います。記述的にもすっきりするし。 http://cs.gogo-asp.net/blogs/naoki/archive/2010/07/02/ASP.NET-MVC-2-_6730AB30B930BF30E0305E5C27606E305C4F106268300130AF30E930A430A230F330C830B530A430C9301C693C8A6E30DD30A430F330C830_.aspx 文献はMVC2ですが、そのまま動きます。 ASP.NET MVC3だと、確かクライアント検証が大幅に拡張されているはずですがそこまで調べてない(^^; ■コントローラメソッドの拡張 ここはやはり、IoCでしょう。 単体テストもできるようになるし、堅牢になるし、捗りますよ。 http://david9142.wordpress.com/2012/07/13/asp-net-mvc%E3%81%AE%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9%E3%82%92%E8%80%83%E3%81%88%E3%81%A6%E3%81%BF%E3%81%9F1/ ですかね。

nayutax
質問者

お礼

shockatzさま、いつも有難うございます。 今回も有効なコンテンツを教えていただき、感謝雨あられです。 最後の「コントローラメソッドの拡張」ですが、全く理解できませんでした!(苦笑 お恥かしい限りです。 こういう記事に敬服するとともに、精進します!

その他の回答 (1)

  • onos
  • ベストアンサー率81% (127/155)
回答No.1

まず、TempDataは使い方を間違ってるように思います。 TempDataというのは一種のSession変数ですので、ビューにデータを渡すのに使うというのはおかしいかと。 使うならViewBagかな? MVCのバージョンによってはViewBagが使えないので、そのときはViewDataかなぁ。 あと、1つめと3つめの質問はある意味同じ回答になるかと。 サンプルとかだとコントローラにいろんな処理いれてしまいがちですが、コントローラはできるだけ薄くするべき、という考え方があったりします。 で、どうするか、というのは、、、そうですねぇ。MVCパターンの話を追いかけてみるとか、かなぁ。 責務をきちんと切り分けてクラスを設計しましょう、とかそういう話になるはず。 検証については、アクションの呼び出しの時点で行われているのはモデルバインダーがやってくれている部分です。 デフォルトのモデルバインダーを使うのではなく、独自のモデルバインダーを使うという方法が一つあります。 そのほうがうえで書いた責務をわける、という意味ではいいようにも思いますが。 コントローラーの中でやるべきかどうかは別として、検証プログラム書いて結果をModelStateに埋めこんでViewに渡すようなこともできます。 とりあえずはそんなとこですかね。

nayutax
質問者

お礼

onos様、いつもありがとうございます。 ViewBagの件ですが、やはりコントローラからViewに返すのが正しい道でしょうね。 ご指摘ありがとうございます。 独自のモデルバインダの件、前回もご指摘いただいています。 正直難しくてよくわかりません。 こういうタイプの検証は頻繁に発生しそうなので、頭が痛いです。

関連するQ&A

  • MVCの処理について

    PHP Version 5.1.6を使っています。 CodeIgniterのバリデーション処理で分からないところがあります。 http://userguide.cilab.info/libraries/validation.html この例の中のindexメソッドです。 if ($this->validation->run() == FALSE) { $this->load->view('myform'); } else { $this->load->view('formsuccess'); } これはエラーがあったらmyformにエラーを付加したのを表示させ、 エラーがなかったらformsuccessページを表示させます。 入力ページ(myform)とその結果ページ(formsuccess)の2つで構成されています。 もし入力ページが2つ以上続いた場合にどのようなプログラムになるのかが分かりません・・・ 例えば、myform1には'ユーザ名'と'パスワード'を入力するフォームがあり myform2は'メールアドレス'を入力するフォームがあって、その次に 結果ページ(formsuccess)という3段階のステップあったとします。 (myform1→myform2→formsuccess) この場合に上の制御プログラムは同じURIで作業するとして $rules['username'] = "required"; $rules['password'] = "required"; $this->validation->set_rules($rules); $fields['username'] = 'ユーザ名'; $fields['password'] = 'パスワード'; $this->validation->set_fields($fields); if ($this->validation->run() == FALSE) { $this->load->view('myform1'); } else { $rules['email'] = "required"; $this->validation->set_rules($rules); $fields['email'] = 'メールアドレス'; $this->validation->set_fields($fields); if ($this->validation->run() == FALSE) { $this->load->view('myform2'); } else { $this->load->view('formsuccess'); } } このようなさらに奥に掘り下げていくようなプログラムになるのでしょうか? これだとなんか変な感じがします・・・

    • 締切済み
    • PHP
  • メールが送信されません。<phpを閉じて開いても

    メールが送信されません。<phpを閉じて開いても送信ボタンを押してメールが送信されません。 <?php namespace Application\Block\ExternalForm\Form\Controller; use Concrete\Core\Controller\AbstractController; use Core; use Page; class TForm2 extends AbstractController { // action を指定しなかった場合、view が実行されると思われる。初期表示。 public function view() { // 最初にフォームを開いた時の処理です。初期値を view に渡して、編集画面の表示を指定します。 $input['p_name'] = ''; $input['p_email'] = ''; $input['p_message'] = ''; $this->set('isvalid', true); $this->set('input', array()); $this->set('error', array()); $this->set('section', 'edit'); } // 入力値の検証処理 private function validate() { // バリデーション結果を成功に設定 $isvalid = true; // 入力値を取得 $input['p_name'] = trim($this->post('p_name')); $input['p_email'] = trim($this->post('p_email')); $input['p_message'] = trim($this->post('p_message')); // 検証実行 if ($input['p_name'] == '') { $isvalid = false; $error['p_name'] = 'お名前は、必ず入力してください。'; } if ($input['p_email'] == '') { $isvalid = false; $error['p_email'] = 'メールアドレスは、必ず入力してください。'; } if ($input['p_message'] == '') { $isvalid = false; $error['p_message'] = 'メッセージは、必ず入力してください。'; } // View に値を渡す $this->set('isvalid', $isvald); $this->set('input', $input); $this->set('error', $error); return $isvalid; } public function action_confirm() { // 検証結果によって、確認(confirm) を、編集(edit) セクションを出し分け。 if ($this->validate()) { $section = 'confirm'; } else { $section = 'edit'; } $this->set('section', $section); } public function action_update() { // 検証結果によって、完了(complete) を、編集(edit) セクションを出し分け。 if ($this->validate()) { //ここの部分でエラーが発生します。 ?>//一回閉じる。 //ここから追加しました。 <?php if ($_POST) { ?> <!-- 確認画面 --> <form action="./tform2.php" method="post"> 名前 <?php echo $_POST["fullname"] ?> Eメール <?php echo $_POST["email"] ?> 本文 <?php echo nl2br($_POST["message"]) ?> <input type="submit" name="back" value="戻る" /> <input type="submit" name="send" value="送信" /> </form> <?php } else { ?> <!-- 入力画面 --> <form action="./tform2.php" method="post"> 名前 <input type="text" name="fullname" value=""> Eメール <input type="email" name="email" value=""> 本文 <textarea name="message" id="" cols="" rows=""></textarea> <input type="submit" name="confirm" value="確認" class="button"> </form> <?php } ?> //ここまで <?php //もう一回開ける $c = Page::getCurrentPage(); header('location: ' . Core::make('helper/navigation')->getLinkToCollection($c, true) . '/complete'); exit; } else { $this->set('section', 'edit'); } } public function action_complete() { $this->set('section', 'complete'); } public function action_back() { // 戻るボタンで、編集(edit)セクションを表示します。 $section = 'edit'; $this->set('section', $section); } }

    • 締切済み
    • PHP
  • アクションスクリプトについて質問です。

    アクションスクリプトについて質問です。 ◆やりたいこと◆ 曜日を取得して、その曜日のフレーム内容を表示させたいです。 ◆試したこと◆ アクションスクリプトをいろいろなサイトを見ながら作ってみました。 myDate = new Date(); myDay = myDate.getDay(); if (myDay == 0) { gotoAndStop("sun"); } else if (myDay == 1) { gotoAndStop("mon"); } else if (myDay == 2) { gotoAndStop("tue"); } else if (myDay == 3) { gotoAndStop("wed"); } else if (myDay == 4) { gotoAndStop("thu"); } else if (myDay == 5) { gotoAndStop("fri"); } else if (myDay == 6) { gotoAndStop("sat"); } ◆不具合内容◆ データは表示されるのですが、myday のsun がまず表示されてしまいます。 ブラウザの更新をかけないと、その曜日のフレームの内容が表示されません。 3台のPCで試しましたが、同様な状態になります。 アクションスクリプトに不具合があるかと思うのですが 無知なため、これ以上のアイデアがでません。 どこを修正すれば良いのかお教えいただけませんでしょうか? こちらの作成ソフトはfrimo 3 で ActionScript2.0、Flash Player8で吐き出ししております。

  • ParseとTryParseを実装するなら

     こんにちは。c#初心者です。  c++にも、c#にもintにParseメソッドと、TryParseメソッドがあると思いますが、intのように比較的簡単に解析できるもの(比較的簡単なメソッド)ならParseメソッドをコピペして、例外の部分を「return false;」に変えるだけで十分だと思うのですが、問題は、Color構造体のような(やや)複雑なものです。  R, G, Bに加え、α値があり、また、名前で色を表す可能性もあります。考えただけで(intと比べると)かなり面倒なメソッドになることが分かると思うのですが、とりあえずParseメソッドを実装したとしましょう。  次にどうせならTryParseメソッドも実装したいところなのですが、初心者が考えた方法は3つあります。 1、Parseメソッドをコピペして(FormatExceptionなどの)例外部分を「return false;」に変える。 2、Parseメソッドをtryブロックに入れて、catchブロックで「return false;」とする。 3、privateなtryParseメソッドを用意し、それをベースにする。戻り値はException型で、Parseメソッドではnullで無ければスロー、TryParseメソッドでは「return exception == null;」とする。 (ソースコードは一番下)  1は保守性上、少々問題ありかと。2は利用側がちょっと楽になることには変わりないが、パフォーマンス重視には向かない(というかテキストを解析している時点でパフォーマンス低いかな?)、3は1、2の問題を克服しているもののこのような設計でよいのか自信なし。  という訳で悩んでいます。どれが一番良いでしょうか? もっと良い案があればそれでも良いです。どなたかわかる方がいらしゃいましたら教えてください。 ----------- 簡易ソースコード -------------- c#で書いています。また読みやすく(なっているかどうかは分かりませんが)するため、1行で書けるところも2行以上で書いている部分もあります。 案1 public static Color Parse(string text) {   ……   if ( invalid ) throw new FormatException();   …… } public static bool TryParse(string text, out Color result) {   ……   if ( invalid ) {     result = Color.Empty;     return false;   }   …… } ------------------------------------------------ 案2 public static Color Parse(string text); // 略 public static bool TryParse(string text, out Color result) {   try {     result = Parse(text);     return true;   }   catch ( FormatException ) {     result = Color.Empty;     return false;   } } --------------------------------------------------- 案3 private static Exception tryParse(string text, out Color result) {   ……   if ( invalid ) {     result = Color.Empty;     return new FormatException(); // スローせずに返す   }   …… } public static Color Parse(string text) {   Color result;   Exception exception = tryParse(text, out result);   if ( exception != null ) throw exception;   else return result; } public static Color TryParse(string text, out Color result) {   Exception exception = tryParse(text, out result);   if ( exception == null ) return true; // 例外が無ければ成功   else return false; }

  • メソッドが値を返すとき

    ”メソッドが値を返さない”というエラーで困っています。 次のようなプログラムでは、メソッドが値を返せないのは当たり前なのでしょうか?? public int A(){ int a= 3; int b= 5; if(条件式){ return (Math.sin(a*x)); }else if(条件式){ return (Math.cos(b*x)); }else if(条件式){ return 式 ; } } 「return」をif文のなかに入れてしまうことが,いけないのでしょうか? また,それがしてはいけない事ならば,条件式によって扱うreturn文を変えるには, どうしたら良いかアドバイスを下さい。 よろしくお願いします。

    • ベストアンサー
    • Java
  • 【ASP.NET MVC】一覧編集画面

    いつもお世話になります。 ASP.NET MVCを使って構築中のサイトで、また暗礁に乗り上げております。 商品一覧の画面で、一覧表示のままCRUD(追加・更新・削除)するインターフェースを客先から求められております。 一覧表示 → 個別画面でのCRUDはできていますが、画面遷移が面倒ということで却下されました。 ASP.NETでいう、GridViewのようなテイストです。 (各行にテキストボックス・更新ボタン・削除ボタン) モデルはこんな感じです(簡略化してあります。実際にはご教示いただいたカスタム検証もあります) public class Lady {  public int ID { get; set; }  public string name { get; set; }  public int areaID { get; set; }  public int salesAmount { get; set; } } public List<Lady> ladies; 一覧表部分のビューはこんな感じです(細かい点は省略です) @foreach (var lady in Model.ladies){  @using(Html.BeginForm("UpdateLady","Admin")) {   @Html.Hidden("ID", lady.ID)   @Html.TextBox("name", lady.name)   @Html.DropDownList("areaID", new SelectList(model.areas, "areaID","areaName", lady.areaID))   @Html.TextBox("salesAmount",lady.salesAmount)   <!-- 以降不明 -->   <input type="submit" value="更新"/>   <input type="submit" value="削除"/>  } } コントローラ class AdminController {  public EditLady(){   var model = new EditLadyView();   return View("EditLady", model);  }  [HttpPost]  public UpdateLady(Lady lady){   if (ModelState.IsValid){    // 更新    // lady.IDをキーにしてDBを更新   } else {    // 再表示 <!-- ここがわからない -->   }  }  [HttpPost]  public DeleteLady(int ID){   // 削除   // IDをキーにしてDBを削除  } } 質問点は以下の通りです。 ■表示はOKですが、できたhtmlを見ると、各行の項目が同じid / name名になります。これで正しいですか? ■同じForm内に更新ボタンが複数あった場合、formのPOST先を変える必要がありますが、javascriptを使うのでしょうか? ■検証失敗時、全体を再表示するにはどういった方法があるでしょうか? 読み直すと入力した値が消えてしまいます。 会社にMVCの経験者がおらず、苦労しています。 なにとぞお助けください。

  • 複数テーブルの更新方法について

    会員情報の登録処理について 会員情報を管理するテーブルが3つあります。 もし、下記の(2)(3)でエラーのとき 中途半端に会員基本情報のみ出来てしまいます。 このときロールバックみたいな機能があるのでしょうか? みなさんは、このような場合どのように対応しているのでしょうか? ご指導よろしくお願いします。 【環境】 MYSQL:ver5 PHP:ver5 他に特にありません テーブル (1)会員基本情報 :staff  ※必ず存在する (2)会員関連情報1:staff_op1  ※ないときもある 画面選択項目で会員ごとに選択件数が違うため別テーブルにしている。  例:好きな○○のジャンルなど複数選択みたいな... (3)会員関連情報2:staff_op2  ※ないときもある  例:好きな○○のジャンルなど複数選択みたいな... //会員基本情報登録 $sql = "INSERT INTO staff ('staff_cd') VALUES ('12345')"; $result=mysql_query($sql); if($result){ //会員基本情報登録がOKのとき //会員関連情報1を登録(実際は選択件数分ループが必要) $sql = "INSERT INTO staff_op1 ('staff_cd','item1') VALUES ('12345','00001')"; $result=mysql_query($sql); if($result){ //会員関連情報1登録がOKのとき //会員関連情報2を登録(実際は選択件数分ループが必要) $sql = "INSERT INTO staff_op2 ('staff_cd','item1') VALUES ('12345','00001')"; $result=mysql_query($sql); if($result){ } else{ $error[] = "insert staff_op2 table err" } } else{ $error[] = "insert staff_op1 table err" } } else{ $error[] = "insert staff table err" } if(!$error){ $error[] = "会員情報登録完了!" }

  • 複数テーブルの更新方法について

    会員情報の登録処理について 会員情報を管理するテーブルが3つあります。 もし、下記の(2)(3)でエラーのとき 中途半端に会員基本情報のみ出来てしまいます。 このときロールバックみたいな機能があるのでしょうか? みなさんは、このような場合どのように対応しているのでしょうか? ご指導よろしくお願いします。 【環境】 MYSQL:ver5 PHP:ver5 他に特にありません テーブル (1)会員基本情報 :staff  ※必ず存在する (2)会員関連情報1:staff_op1  ※ないときもある 画面選択項目で会員ごとに選択件数が違うため別テーブルにしている。  例:好きな○○のジャンルなど複数選択みたいな... (3)会員関連情報2:staff_op2  ※ないときもある  例:好きな○○のジャンルなど複数選択みたいな... //会員基本情報登録 $sql = "INSERT INTO staff ('staff_cd') VALUES ('12345')"; $result=mysql_query($sql); if($result){ //会員基本情報登録がOKのとき //会員関連情報1を登録(実際は選択件数分ループが必要) $sql = "INSERT INTO staff_op1 ('staff_cd','item1') VALUES ('12345','00001')"; $result=mysql_query($sql); if($result){ //会員関連情報1登録がOKのとき //会員関連情報2を登録(実際は選択件数分ループが必要) $sql = "INSERT INTO staff_op2 ('staff_cd','item1') VALUES ('12345','00001')"; $result=mysql_query($sql); if($result){ } else{ $error[] = "insert staff_op2 table err" } } else{ $error[] = "insert staff_op1 table err" } } else{ $error[] = "insert staff table err" } if(!$error){ $error[] = "会員情報登録完了!" }

    • 締切済み
    • PHP
  • ArrayList の継承について(初心者です)

    買い物かごクラスのプログラムを作っています。 このソースはフィールドにListを持たせているのですが、ArrayList を継承 してCartクラスを作りたいです。そうすることで、CartクラスのaddItem() メソッドやremoveItem()メソッドはmainでメソッドを呼び出せば良いため、 不要になると思うのですが、合計金額を求めるときに、どのように商品値段 を参照していいのかわかりません。 また、ArrayList を継承する場合、public class Cart extends ArrayList という書き方で良いのでしょうか? public class Cart { List<Item> itemList = new ArrayList<Item>(); public ShoppingCart(){} //商品をカートに追加 public void addItem(Item item){ itemList.add(item); } //カート内に入っている商品の合計金額を返却 public int getTotalPrice(){ int total=0; for(int i=0;i<getItemSize();i++){ total+=itemList.get(i).getPrice(); } return total; } //リストの指定された位置にある要素を削除 public void removeItem(int index){ itemList.remove(index); } }

  • Javaについて質問します。

    全員のjudgeBMIの適用結果を表示させたいのですが 下から4行目のcalcBMIにエラーが出てしまいます。 どこを修正すればいいかわからなく困っています。 わかる方いましたら教えていただきたく質問しました。 public class Enshu14_3 { private static double calcBMI(double weights, double heights){ return(weights/(heights*heights)); } private static String judgeBMI(double bmi){ String result; if(bmi<18.5){ result="やせ"; }else if(30.0<=bmi){ result="高度肥満"; }else if(25.0<=bmi){ result="肥満"; }else{ result="標準"; } return result; } public static void main(String[] args){ double[] weights = {64.0,51.5,70.2,58.3,85.4}; double[] heights = {1.65,1.67,1.66,1.75,1.68}; for(int i=0; i<weights.length; i++){ System.out.println(i+"番目の人は"+judgeBMI(calcBMI(weights,heights))+"です。"); } } }

専門家に質問してみよう