部分视图 vs. Json(或两者兼有)

22

我使用ASP.NET MVC和jQuery,有很多Ajax请求发送到我的控制器。

使用Partial Views(用户控件)来构建页面加载时的初始视图。然后,如果需要根据Ajax请求附加/替换数据,则从Json响应构建HTML。

这种方法给了我完全的控制权,例如,如果某些事情出了问题,我可以从我的控制器中获取额外的信息,然后基于此显示错误消息。

然而,最近我非常烦恼在维护部分视图和从Json生成HTML中所需的所有额外工作。

我喜欢发起一个jQuery ajax请求,然后让控制器返回PartialView("mypartialview"),然后只需使用jQuery将视图中的HTML替换即可。

但是,通过这种方式,我无法附加来自控制器的额外数据 - 它要么是PartialView给我的内容,要么就什么都没有。至少目前是这样。

如果在控制器动作的某个阶段出现验证错误,我不想返回部分视图的HTML。

那么如何处理这个问题呢?

感谢您的阅读。

4个回答

20

根据这个stackoverflow答案,我刚刚开始做同样的事情。

首先为控制器类创建一个扩展方法。

public static string RenderViewToString(this Controller controller, string viewName, object model)
{
    using (var writer = new StringWriter())
    {
         var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
         controller.ViewData.Model = model;
         var viewCxt = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, writer);
         viewCxt.View.Render(viewCxt, writer);
         return writer.ToString();
     }
}

然后在控制器的操作方法中返回json。
return Json(new {
  Html = this.RenderViewToString("MyView", model),
    SomeExtraData = data
});

现在您的ajax请求将收到包含html的json。仍在尝试这种方法,而不是返回纯Html片段。

希望这有所帮助。

编辑 更新以适用于razor。


4
这是一种利用每个结果返回的 Content-Type 的方法。我们正在使用此方法发送错误信息,已经有JS来显示消息,因此我们要么得到所需的部分视图,要么得到用于错误报告等控制信息。
供参考,部分视图返回 text/html,JSON响应应返回 application/json
像往常一样,有趣的部分在JavaScript端,JQuery的ajax()在这里不会让人失望!
在您的控制器中,根据需要返回PartialView()Json(model,); 我们使用它在 try/catch 格式中。
public ActionResult Edit(int id) {
    try {
        var model = getYourModel();
        return PartialView("Edit", model);
    }
    catch (Exception ex) {
        var mi = new MessageInfo(MessageType.Error, String.Format("Edit failed: {0}", ex.Message), true);
        return Json(mi, "application/json", JsonRequestBehavior.AllowGet);
    }
}

在JS方面,我们使用以下函数。请注意,您需要重新建立任何在初始页面级GET中挂钩的JQuery事件,因此我们有一个回调参数。
function getPartialView(action, controller, model, divId, callback) {
    var url = "/" + controller + "/" + action + "/";
    $.ajax({
        type: "GET",
        url: url,
        data: model,
        success: function (data, textStatus, jqXHR) {
            var ct = jqXHR.getResponseHeader("Content-Type");
            var mx = ct.match("text\/html");
            if (mx != null) {
                $(divId).html(data);
                if (callback) {
                    callback($(divId));
                }
            }
            else {
                addMessage(data.type, data.title, data.text, data.sticky);
            }
        },
        error: function (jqXHR, textStatus, errorThrown) {
            addMessage(3, "\"" + url + "\": Failed", textStatus + ": " + errorThrown, false);
        }
    });
}

唯一棘手的部分是检查响应中的Content-Type头,并相应地做出反应。请注意,如果不是HTML,我们会“作弊”并假定为JSON。我们调用我们预先存在的addMessage()函数,无论您需要做什么!最后,这里有一个使用onclick指向上面的getPartialView()的示例Anchor元素。
<a href="#" onclick="getPartialView('Action', 'Controller', model, '#partialviewdivid', function(dvx) { connectJqueryEvents(dvx); })">Cancel</a>

非常好用...

除了通过Ajax.BeginForm()提交表单时,由于Content-Type验证不足而错误地将JSON负载视为HTML来处理。结果是您的

添加了一些JSON,它基本上不会呈现为HTML。 AjaxOptions.OnSuccess回调确实执行了,但此时对于您的DOM来说已经太晚了!

有一个简单的解决方案,但不幸的是,它需要对jquery-unobtrusive-ajax.js进行小的修复,因为asyncOnSuccess()函数编写时视野狭窄。

function asyncOnSuccess(element, data, contentType) {
  var mode;

  if (contentType.indexOf("application/x-javascript") !== -1) {
    return;
  }
  if (contentType.indexOf("application/json") !== -1) {
    return;
  }
...snip...
}

在OOTB版本中,第二个if语句缺失;添加该语句是修复它不会把你的JSON有效载荷猛烈地插入DOM所必需的。
有了这个修复,JSON有效载荷就能传递到你的AjaxOptions.OnSuccess Javascript中,然后你可以按照需要进行操作。
希望你知道,由于你正在发送Json,你可以发送任何类型的模型,并让Javascript对其进行排序;hasOwnProperty()在这方面非常有用。因此,你显然可以通过前面提到的RenderViewToString()方法发送一些视图HTML。

2
我相信您可以将呈现的HTML作为字符串返回 - 这也可以是包含要显示的错误消息的HTML字符串?

1
确实,这正是 PartialView 响应的作用。虽然你没有详细说明,但解决方法是:通过使用与最初呈现页面部分相同的用户控件来使你的 AJAX 方法返回渲染后的 PartialView。你可以编写 return PartialView(model) 而不是 return Json(model) 来实现这一点。 - Craig Stuntz
谢谢。这的确是一个更好的解释 :) - Paddy
Craig,这就是我现在所做的。我的问题是我想同时返回Html和Json - 或者换句话说:我想要从return PartialView返回的结果html,然后将其包装在Json中,以便我也可以发送其他数据。有点像这个人: https://dev59.com/_UjSa4cB1Zd3GeqPGY0r我猜上面的方法会起作用,但我想知道其他人如何处理这个问题。 - bgeek
你可以(轻松地)将同一模型作为部分视图(HTML 片段)或 JSON 返回。构建模型,然后调用所需的方法。但是不要编写 JavaScript 从相同模型的 JSON 序列化中复制您的部分视图。 - Craig Stuntz

0

你也可以这样做,将它放在你的控制器里面。

protected string RenderPartialViewToString(string viewName, object model)
{
    if (string.IsNullOrEmpty(viewName))
        viewName = ControllerContext.RouteData.GetRequiredString("action");

    ViewData.Model = model;

    using (StringWriter sw = new StringWriter()) {
        ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
        ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
        viewResult.View.Render(viewContext, sw);

        return sw.GetStringBuilder().ToString();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接