5

我对 MVC 非常陌生,并且在尝试将现有站点移植到 MVC4 时遇到了这个问题。

我们使用的模型中大部分数据由服务调用填充,因此显然我们希望将调用保持在最低限度。问题是当我尝试将模型传回控制器时,模型中的复杂对象总是变为空。我已经能够使用 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/Index 页面(这是从 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

我不确定我是否完全理解了您的问题,但我可以告诉您,您需要将模型的每个值放在一个表单中,该表单发布到您希望为空的控制器操作。

正是您在 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 将其转换为 HTTP POST 请求的字符串:

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

这将导致 的新实例,ComplexModelSubModel属性设置为 的新实例,ComplexSubModel其属性StringList设置为System.Collection.Generic.List<string>包含三个字符串First entrySecond entry的新实例Third 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])
}

回发中包含的每个属性都不会为空,但可能是用户伪造的,因为假设它们在隐藏字段中呈现,它们很容易重新传输到服务器。事实上,您无法确定重新传输的值是您之前通过服务调用获取的值。

另一种可能性是将服务调用的结果保存在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(...);
}

这两种变体都有优缺点,例如在一个浏览器会话中浏览两个选项卡时它们可能会出现问题,或者当您使用会话状态服务器时需要对类进行可序列化。然而,程序总是一样的:你要么必须

  • 每次需要时从服务中获取数据(这很昂贵)或
  • 将它们保存在服务器上的任何位置(TempData、Session 和其他东西)或其他
  • 用表单提交它们(可以由用户伪造,并不总是那么容易)。

选择你的毒药。;-)

于 2012-11-19T19:43:40.180 回答