在ASP.Net MVC中,可以将ModelState与ajax更新一起使用吗?

11

这是我之前关于将错误传递回客户端并涉及ModelState的问题的后续,参考此前的提问

有人成功地使用Nerd Dinner方法实现了Ajax操作吗?因此,Nerd Dinner执行以下更新。

[AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Edit(int id, FormCollection formValues) 
{
    Dinner dinner = dinnerRepository.GetDinner(id);
    try 
    {
        UpdateModel(dinner);
        dinnerRepository.Save();
        return RedirectToAction("Details", new { id=dinner.DinnerID });
    }
    catch 
    {
        foreach (var issue in dinner.GetRuleViolations()) {
        ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
    }
        return View(dinner);
    }
}

使用 jQuery $.ajax

function hijack(form, callback, errorFunction, format) {
    $.ajax({
        url: form.action,
        type: form.method,
        dataType: format,
        data: $(form).serialize(),
        success: callback,
        error: function(xhr, textStatus, errorThrown) {
            errorFunction(xhr, textStatus, errorThrown);
        }
    });
}

Ajax,控制器的“try”部分变为:

    try 
{
    UpdateModel(dinner);
    dinnerRepository.Save();
    return PartialView("PartialDetails", new { id=dinner.DinnerID });
}

不过catch部分该如何处理呢?

一个简单的错误处理方案是发送一个错误信息:

catch(Exception ex)
{
    Response.StatusCode = 500;                
    return Content("An Error occured.");
    //throw ex;
}

但是这个错误消息没有通过MVC内置的健壮模型状态。我考虑了许多选项,但我真正想要的有两点:

  1. 我想在jQuery的error属性中处理错误。
  2. 我希望尽可能使用内置的ASP.Net MVC验证逻辑。

这可行吗?如果不行,你知道什么更好的替代方案吗?

非常感谢。

更新 我还没有将此标记为已回答,因为我还没有实现我认为最有效的方法。

我决定不再采用“成功=>发送刷新列表,失败=>发送错误消息”的方法。我这样做是为了减少调用次数,但是刷新列表确实被设置为页面了。尝试同时执行这两个任务会将弹出框紧密绑定到其整体页面上。

我将添加一个自定义jQuery事件,在对话框关闭时刷新主页面列表。本质上,它是观察者模式。我喜欢这个想法,即页面对弹出窗口说“告诉我你完成了”(也就是关闭),而不必告诉弹出窗口原因。它确实需要额外的调用,但我不认为这是一个大问题。

我仍然不确定我喜欢/不喜欢服务器端验证,并且正在考虑仅使用客户端验证。虽然服务器端验证看起来像是一个清晰的分层,但它也有许多问题,包括:

1)它在最后才进行质量检查,而不是在构建的过程中进行检查。类比于制造业的话,这就像一辆汽车到达经销商时才进行测试,而不是在制造过程的各个环节中进行测试。
2)它违反了Ajax的意图。Ajax不仅涉及异步事件的发送,还涉及只发送我需要的内容并只接收我需要的内容。为了提供错误详细信息,将整个模型状态发送回去似乎不符合Ajax的要求。

我想做的是仅使用客户端验证,但可以使用服务器端代码和自定义视图模型告诉客户端如何动态创建这些验证规则。

我还怀疑像IronRuby或IronPython这样的动态语言可能会提供更优雅的解决这些问题的方法,但我可能需要更长时间才能研究这种可能性。


我觉得这要看具体情况,如果进行两个请求不是问题的话,我会选择这种方式。就个人而言,使用JavaScript在客户端进行大量验证并不是我喜欢的事情(我不知道为什么,但我认为js不是很可靠/安全/等同地实现(在这一点上jquery拯救了我们)),特别是因为有时候你不能在客户端进行所有的验证,你必须使用一些服务器端的检查,例如(此实体已经存在于数据库中?),并支持禁用js的客户端,但正如我之前所说,这要看具体情况。 - JOBG
我同意你不能在客户端进行所有验证,因为验证框架似乎主要针对字段错误(过长,不是日期等)。至少在我看到的示例中是这样的。当您检查用户输入时,Javascript 似乎是适当的地方来进行检查。 - John
我已经坚定了我的决定,要进行两个调用而不是一个,因为这样可以将细节部分视图与其下方的页面视图解耦。我希望弹出窗口返回页面视图“我已关闭”,但并不知道页面视图将要做什么。jQuery应该能够轻松实现这一点。这使我不必担心我的细节部分视图的更改会影响页面,反之亦然。 - John
2个回答

4
如果我理解你的意图,我的第一个答案将是不行,你不能通过Ajax请求直接使用模型状态。您可以尝试模拟ModelState的行为来显示错误:
1. 通过JSON传递List>(属性,消息)(这将要求您将ModelState中的modelErrors传递给新结构),并通过JS / jQuery构建验证摘要的HTML(我认为这是一种过度解决方案)。 2. 如果您要去服务器,并且有任何错误,请仅呈现Html.ValidationSummary(),通过JSON传递它,并将其预置到表单中。如果一切正常,请返回PartialDetails视图并替换实际内容。这将需要某种状态参数,以便您知道在ajax回调上从服务器返回了什么。

编辑:最后一种选项听起来不错,但有些棘手,因为您需要通过JSONResult以字符串形式返回部分视图。这里有一个关于该技巧的问题和解决方案:如何将ASP.NET MVC视图呈现为字符串?

就我个人而言,我认为使用错误属性根本没有任何好处。我只在非常特定的情况下使用它,例如超时错误和服务器异常,而不是应用程序异常。

编辑:

使用JSON:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues)
{
    Dinner dinner = dinnerRepository.GetDinner(id);
    try
    {
        UpdateModel(dinner);
        dinnerRepository.Save();
        return Json(new 
        {
            result = "success",
            html = this.RenderToString("PartialDetails", dinner) 
        });

    }
    catch
    {
        foreach (var issue in dinner.GetRuleViolations())
        {
            ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
        }
        return Json(new
        {
            result = "failed",
            html = this.RenderToString("PartialEdit", dinner)
        });
    }
}

在这里,result参数将告诉您在每种情况下要执行的操作,只需在回调中检查它即可。

谢谢。然而,挑战的一部分是我真的更喜欢将结果作为PartialView结果返回,而不是JSON结果。对于第二条,您是否正在进行两个调用?一个用于返回成功/错误信息,另一个用于传递结果?这是我考虑的一个选项。 - John
这个结构只需要一个调用,就像这样 {"status": [{"code": "error"}], "html" : [{"html":"a lot of html"}]}。如果你想让它成为一个部分视图结果对象,为什么不在模型出现错误的情况下返回整个Edit部分视图,而在没有错误发生时返回Details部分视图,这样模型状态就可以正常工作了。 - JOBG
我的情况与大多数示例不同的部分是,我想根据成功或错误执行两个非常不同的操作。如果出现错误,我可以返回Edit partial View并重新创建对话框。但是,如果成功了,我想关闭对话框并刷新主页面上的列表。因此,客户端代码需要知道它是成功还是失败,以便知道要更新哪个对象--对话框还是列表。 - John
在这种情况下,我建议您选择JSONResult类型,因为普通的PartialView类型不会给您任何关于服务器端发生了什么状态引用,除非您想要进行两个不同的请求,一个用于状态,另一个用于PartialView。 我已经使用Json方法更新了帖子。 - JOBG

2

我来分享一下我的MVC3做法。使用你的编辑方法,你可以这样做:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues) 
{
    Dinner dinner = dinnerRepository.GetDinner(id);
    try 
    {
        UpdateModel(dinner);
        dinnerRepository.Save();
        return Json (new { Success = true, Url = Url.Action("DetailsPartial", dinner), Div = "#DivToUpdateId" }); 
    }
    catch 
    {
        foreach (var issue in dinner.GetRuleViolations()) {
        ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
    }
        //I am replacing this with a partial view which will contain the model state errors. For people using MVC 3 with razor they should be able to use their normal views as partials
        return PartialView(dinner);
    }
}

然后您可以使用类似的成功函数

success: function(data) {
    if (data.Success) {
        $.post(data.Url, function(partial) { 
            $(data.Div).html(partial);
        });
    }
    else
    {
        $('#formDiv').html(data)
    }
}

基本上,如果结果是Json,则数据.Success为true。然后,它会使用Json中指定的div(data.Div)更新结果和Url中指定的操作的div。如果结果不是Json,则是因为post方法失败,formDiv将更新为包含ModelState错误的部分表单。这是我在对话框中使用的方法,但效果非常好。显然,在MVC3中,我会更改一些编辑方法,例如使用TryUpdateModel等,但只是试图给出我的方法的一个示例。通过传递url并发布方法的结果,无需尝试将html呈现为字符串以传递部分内容。

有趣。你能贴出你的Edit PartialView的代码吗? - John

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