5

私はMVCを初めて使用し、既存のサイトをMVC4に移植しようとしたときにこの問題が発生しました。

データの多くがサービス呼び出しによって入力されるモデルを使用しているため、呼び出しを最小限に抑えたいことは明らかです。問題は、モデルをコントローラーに戻そうとすると、モデル内の複雑なオブジェクトが常にnullになることです。ajaxを使用して、コントローラーへのコールバックでデータを永続化することができました。ただし、新しいビューを返すためのアクションが必要であり、アクションが完了すると、ビューのコードが実行されますが、リダイレクトはありません(これは、ajaxのポイントであると私は信じています。私が求めているのは、同じ方法でデータを永続化するが、実際にはリダイレクトするソリューション)。

これが私のモデルです:

public class DistributionModel
{
    public string typeOfDistribution { get; set; }
    public Document document { get; set; }
    public string thumbnailUrl { get; set; }
    public MergeFieldModel mergeFields { get; set; }
}

public class MergeFieldModel
{
    public MergeFields documentMergeFields { get; set; }
}

これが私が使用しているコントローラーアクションです:

        public ActionResult Index(DistributionModel distributionModel)
    {
        distributionModel.mergeFields = new MergeFieldModel() { documentMergeFields = MergeFieldsHelper.GetDocumentMergeFields(distributionModel.document.Id) };
        return View("Index", distributionModel);
    }

下のブロックのボタンの代わりにhref=@Url.Action("Index"、Model)を使用してコントローラーを呼び出し、リダイレクトを実行しようとしました(リダイレクト自体は機能しましたが、その後、内で別のサービス呼び出しを実行する必要がありましたモデル内のDocumentオブジェクトがコントローラーにNULLとして返され続けたため、コントローラーは、呼び出し元のビューから操作していたのと同じドキュメントを取得します。

これがコントローラーを呼び出して実際に完全なモデルを返すビューの部分です:私が探しているのは、配布/インデックスページへのリダイレクトを取得できるようにajaxなしでこれを達成する方法だと思います(これはDistribution / DocumentDetailsページから起動)

        <button id="EmailDistribution" data-corners="false" data-theme="a">EMAIL</button>

         $('#EmailDistribution').click(function () {
              var model = @Html.Raw(Json.Encode(Model));
              $.ajax({
              url: '@Url.Action("Index", "Distribution")',
              type: 'POST',
              contentType: 'application/json; charset=utf-8',
              data: JSON.stringify(model),     
              processData: false,                 
              });                
         });

おかげで、どんな助けでも大歓迎です。

4

1 に答える 1

8

問題を正確に理解したかどうかはわかりませんが、モデルのすべての値を、nullにしたくないコントローラーアクションに投稿された形式で入力する必要があると言えます。

これは、ajax呼び出しで行うこととまったく同じです。現在、モデル全体をjsonに変換し、jQuery機能を使用してモデルを再度変換してデータを投稿します。たとえば、次のモデルがあるとします。

public class TestModel {
    public string A { get; set; }
    public string B { get; set; }
}

javascriptコードは、{ A: 'Value for a', B: 'Value for B' }jQueryを使用してHTTPPOSTリクエストに変換されるのと同様の文字列を作成します。

POST /Controller/Index HTTP/1.1
Host: demo.loc
User-Agent: Mozilla/5.0 whatever
Content-Type: application/x-www-form-urlencoded; charset=utf-8

A=Value+for+a&B=Value+for+B

その結果、Indexアクションが呼び出されDefaultModelBinder、値がモデルプロパティにバインドされます。これは、整数などのプリミティブ型だけでなく、コレクションなどの複雑な型でも機能します。はこれらのタイプのDefaultModelBinder変換を処理します。

より複雑なモデルを見てみましょう。

public class ComplexSubModel {
    public ICollection<string> StringList { get; set; }
}

public class ComplexModel {
    public ComplexSubModel SubModel { get; set; }
}

は、次のDefaultModelBinderようなモデルをバインドすることもできます。

POST /Controller/Index HTTP/1.1
Host: demo.loc
User-Agent: Mozilla/5.0 whatever
Content-Type: application/x-www-form-urlencoded; charset=utf-8

ComplexModel.SubModel.StringList[0]=First+entry&ComplexModel.SubModel.StringList[1]=Second+entry&ComplexModel.SubModel.StringList[2]=Third+entry

これにより、ComplexModelプロパティSubModelが新しいインスタンスに設定されたの新しいインスタンスが作成されComplexSubModel、プロパティが3つの文字列とを含むStringList新しいインスタンスに設定されます。System.Collection.Generic.List<string>First entrySecond entryThird entry

次に、モデルプロパティを非表示フィールドにレンダリングして、ポストバックに含めるようにします。

@using (Html.BeginForm()) {
    @Html.HiddenFor(m => m.SubModel.StringList[0])
    @Html.HiddenFor(m => m.SubModel.StringList[1])
    @Html.HiddenFor(m => m.SubModel.StringList[2])
}

ポストバックに含まれるすべてのプロパティはnullにはなりませんが、非表示フィールドでレンダリングされたと仮定してサーバーに単純に再送信されるため、ユーザーによって偽造された可能性があります。実際、再送信された値が以前にサービス呼び出しによってフェッチされた値であるかどうかを確認することはできません。

別の可能性は、サービス呼び出しの結果をTempData-dictionaryに保存することです。これは、実際には値をユーザーセッションに格納し、ポストバックアクションで再読み取りされるとすぐに破棄するか、値を直接に格納します。セッション:

public ActionResult Index() {
    // Do service calls

    #region Variant a
    TempData["ServiceResultA"] = foo;
    TempData["ServiceResultB"] = bar;
    #endregion

    #region Variant b
    Session["ServiceResultA"] = foo;
    Session["ServiceResultB"] = bar;
    #endregion

    var model = new DistributionModel();
    // Set properties and stuff

    return View("Index", model);
}

[HttpPost]
public ActionResult Index(DistributionModel model) {
    // Read "cached" service calls

    #region Variant a
    var foo = (TResultA)TempData["ServiceResultA"];
    var bar = (TResultB)TempData["ServiceResultB"];
    #endregion

    #region Variant b
    var foo = (TResultA)Session["ServiceResultA"];
    var bar = (TResultB)Session["ServiceResultB"];
    #endregion

    // Do stuff

    return RedirectToAction(...);
}

どちらのバリアントにも長所と短所があります。たとえば、1つのブラウザセッション内で2つのタブを参照する場合に問題が発生する場合や、セッション状態サーバーを使用する場合にクラスをシリアル化できる必要がある場合などです。それにもかかわらず、手順は常に同じです:あなたはどちらかをしなければならないでしょう

  • サービスから必要なときにデータをフェッチする(高価なもの)または
  • サーバー上の任意の場所(TempData、Sessionなど)に保存するか、
  • フォームを使用して送信します(ユーザーが偽造することはできますが、必ずしも簡単ではありません)。

あなたの毒を選んでください。;-)

于 2012-11-19T19:43:40.180 に答える