MVC、jQuery和处理错误的最佳实践

时间:2021-05-10 20:19:07

Does anyone have a elegant way of dealing with errors in ASP.Net MVC? I constantly run into issues when dealing with requests to controller actions where the Action can be used for both normal requests and AJAX requests. The problem I have is finding an elegant way of dealing with these issues.

有没有人有一个优雅的方法来处理ASP中的错误。净MVC吗?在处理对controller操作的请求时,我经常遇到一些问题,在这些操作可以用于正常的请求和AJAX请求。我的问题是找到一种优雅的方式来处理这些问题。

For example, how could I handle validation errors? Ideally I would like to submit the form to a server via AJAX and then return any errors the action threw and display them on the page, but for the same to work via a normal postback when the client has JavaScript turned off. I know I can use the jQuery Validation Plugin as they type but it's isn't the same, nor is it ideal considering the restrictions on the data to be validated would be specified in two places (my nHibernate Validation Mappings and in the JavaScript file).

例如,如何处理验证错误?理想我想通过AJAX提交表单到服务器,然后返回任何错误操作了并显示在页面上,但是对于相同的工作通过一个正常的回发时客户端JavaScript关掉它。我知道我可以使用jQuery验证插件,但不是同一类型,也不是理想考虑限制指定要验证的数据将会在两个地方(我nHibernate验证映射和JavaScript文件)。

How about when the user requests a non-existent record? Should I redirect to a 404 page? What if the request was made via Ajax (say, to load in a dialogue box).

如果用户请求不存在的记录呢?我应该重定向到404页面吗?如果请求是通过Ajax发出的(例如,在对话框中加载)会怎么样?

So:

所以:

How do you handle errors thrown by controller actions when they were called using Ajax? Especially Model State errors (i.e validation). Can I send it via JSON?

当使用Ajax调用控制器动作时,如何处理它们抛出的错误?特别是模型状态错误(i)。e验证)。我可以通过JSON发送吗?

Do you have tips on how to make controller actions which work well when called normally and via Ajax? This is a annoying problem when writing action methods. Because of the return type I may want a different result depending on the caller. Is there anyway to do this without having two Action Methods?

关于如何使控制器动作在正常调用和通过Ajax调用时运行良好,您有什么建议吗?这是写操作方法时的一个烦人的问题。由于返回类型,我可能需要不同的结果取决于调用者。如果没有两种操作方法,有什么方法可以做到这一点吗?

What is your general strategy for handling errors in actions on MVC? Do you redirect to error pages a lot? Do you redirect to another page?

您处理MVC操作中的错误的一般策略是什么?你经常重定向到错误页面吗?你重定向到另一个页面吗?

Edit: I think part of the problem is I want different things to happen, so if there is an error I would want to stop any progress until it's fix and therefore send a error back. Otherwise I may want to update different areas of the page. But if I had one return how would I know it's a success or failure without wrapping it an object that has a property indicting so (thus making it more difficult to use partial views)

编辑:我认为问题的一部分是我想要不同的事情发生,所以如果有错误,我想要停止任何进展,直到它修复,因此发送一个错误回来。否则,我可能想要更新页面的不同区域。但是,如果我有一个返回,我怎么知道它是成功还是失败呢?如果不将它包装成一个具有属性指示的对象(这样就更难使用部分视图)

Thanks

谢谢

7 个解决方案

#1


3  

The AJAX call is not a page refresh so I would definitely not redirect to a 403, 404 etc. I would display a client side dialog explaining the unexpected result of the request appropriately. Your controller can return the results of failed validation to the AJAX call in a similar fashion to how it would return success data so your dialog can also display whatever is required in this scenario.

AJAX调用不是页面刷新,所以我绝对不会重定向到403、404等。我将显示一个客户端对话框,以适当地解释请求的意外结果。您的控制器可以以类似于返回成功数据的方式将失败验证的结果返回给AJAX调用,这样您的对话框也可以显示此场景所需的任何内容。

#2


2  

Do you have tips on how to make controller actions which work well when called normally and via Ajax? This is a annoying problem when writing action methods.

关于如何使控制器动作在正常调用和通过Ajax调用时运行良好,您有什么建议吗?这是写操作方法时的一个烦人的问题。

Yes, yes I do. We also worked through a similar problem--we wanted the app to have a bunch of forms which would be called generally via ajax but could get hit normally. Moreover, we didn't want a whole bunch of duplicate logic in javascript floating around. Anyhow, the technique we came up with was to use a pair of ActionFilterAttributes to intercept the forms, then a little javascript to wire up the form for ajax handling.

是的,是的,我喜欢。我们还解决了一个类似的问题——我们希望应用程序有一堆表单,这些表单通常通过ajax调用,但通常会受到攻击。此外,我们不希望javascript中出现大量重复的逻辑。总之,我们想到的技术是使用一对ActionFilterAttributes来拦截表单,然后使用一些javascript来连接表单以进行ajax处理。

First, for the ajax request, we just wanted to swap out master pages:

首先,对于ajax请求,我们只想交换母版页:

    private readonly string masterToReplace;

    /// <summary>
    /// Initializes an Ajax Master Page Switcharoo
    /// </summary>
    /// <param name="ajaxMaster">Master page for ajax requests</param>
    public AjaxMasterPageInjectorAttribute(string ajaxMaster)
    {
        this.masterToReplace = ajaxMaster;
    }

    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        if (!filterContext.HttpContext.Request.IsAjaxRequest() || !(filterContext.Result is ViewResult)) return;
        ViewResult vr = (ViewResult) filterContext.Result;
        vr.MasterName = masterToReplace;
    }
}

On the return side, we use xVal to validate client-side, so one shouldn't get much in the way of invalid data, but one can still get some. To that end, we just use normal validation and have the aciton method return the form with validation messages. Successful posts are rewarded with redirects in general. In any case, we do a little json injection for the success case:

在返回端,我们使用xVal来验证客户端,所以不应该有太多无效数据,但是仍然可以得到一些。为此,我们只使用常规验证,并让aciton方法返回带有验证消息的表单。成功的职位通常会得到重定向。在任何情况下,我们都会为成功案例做一些json注入:

/// <summary>
/// Intercepts the response and stuffs in Json commands if the request is ajax and the request returns a RedirectToRoute result.
/// </summary>
public class JsonUpdateInterceptorAttribute : ActionFilterAttribute 
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            JsonResult jr = new JsonResult();
            if (filterContext.Result is RedirectResult)
            {
                RedirectResult rr = (RedirectResult) filterContext.Result;
                jr.Data = new {command = "redirect", content = rr.Url};
            }
            if (filterContext.Result is RedirectToRouteResult)
            {
                RedirectToRouteResult rrr = (RedirectToRouteResult) filterContext.Result;
                VirtualPathData vpd = RouteTable.Routes.GetVirtualPath(filterContext.RequestContext, rrr.RouteValues);
                jr.Data = new {command = "redirect", content = vpd.VirtualPath};
            }
            if (jr.Data != null)
            {
                filterContext.Result = jr;
            }
        }
    }
}

The final trick is using a little javascript object to tie everything together:

最后一个技巧是使用一个小javascript对象将所有的东西连接在一起:

function AjaxFormSwitcher(form, outputTarget, doValidation) {
    this.doValidation = doValidation;
    this.outputTarget = outputTarget;
    this.targetForm = form;
}

AjaxFormSwitcher.prototype.switchFormToAjax = function() {
    var afs = this;
    var opts = {
        beforeSubmit: this.doValidation ? afs.checkValidation : null,
        complete: function(xmlHttp, status){ afs.processResult(afs, xmlHttp, status); },
        clearForm: false
    };
    this.targetForm.ajaxForm(opts);
}

AjaxFormSwitcher.prototype.checkValidation = function(formData, jqForm, options) {
    jqForm.validate();
    return jqForm.valid();
}

AjaxFormSwitcher.prototype.processResult = function(afs, xmlHttp, status) {
    if (xmlHttp == null) return;
    var r = xmlHttp;
    var c = r.getResponseHeader("content-type");
    if (c.match("json") != null) {
        var json = eval("(" + r.responseText + ")");
        afs.processJsonRedirect(json);
    }
    if (c.match("html") != null) {
        afs.outputTarget.html(r.responseText);
    }
}

AjaxFormSwitcher.prototype.processJsonRedirect = function(data) {
    if (data!=null) {
        switch (data.command) {
            case 'redirect':
                window.location.href = data.content;
                break;
        }
    }
}

That little script handles stuff like wiring up the form to do ajax and processing the result (either json command or html which gets displayed in the target). I admittedly suck at writing javascript, so there is probably a much more graceful way to write that.

这个小脚本处理像连接表单这样的东西来执行ajax和处理结果(json命令或html,它们都显示在目标中)。我承认我在编写javascript方面很糟糕,因此可能有一种更优雅的方式来编写它。

#3


1  

Disclaimer: I haven't done much ASP.NET MVC Programming.

免责声明:我没有做太多ASP。净MVC编程。

That said, I've used a number of MVC frameworks in other languages, and have completed quite a few django projects. Over time, I've kinda evolved a pattern to deal with this sort of thing in django, using template inclusion.

也就是说,我使用了许多其他语言的MVC框架,并完成了相当多的django项目。随着时间的推移,我已经演化出一种模式来处理django中的这类问题,使用模板包含。

Essentially, I create a partial template containing the form (and any validation errors), which gets included in the main template. The view (or Controller, in your case) then selects between these two templates: AJAX requests get the partial template, and regular requests get the full template. In the main template, I use the jQuery form plugin to submit the form via AJAX, and simply replace the current form with the data from the server: if validation fails, I get a form with highlighted fields, and a list of errors on top; if the POST succeeds, the form is replaced with a success message, or whatever is more appropriate for your situation.

本质上,我创建了一个包含表单(以及任何验证错误)的部分模板,它包含在主模板中。然后在这两个模板之间选择视图(或控制器):AJAX请求得到部分模板,常规请求得到完整的模板。在主模板中,我使用jQuery表单插件通过AJAX提交表单,并简单地用服务器上的数据替换当前表单:如果验证失败,就会得到一个带有高亮字段的表单,以及顶部出现的错误列表;如果文章成功,表单将被替换为成功消息,或者任何更适合您的情况的消息。

I guess in the ASP.NET MVC world, UserControls could be the equivalent of partials? This article shows how to acheive something similar to what I've described (though, the article seems rather dated, and things could've changed).

我猜是ASP。NET MVC世界,用户控件可以等同于部分吗?本文展示了如何实现与我所描述的类似的东西(尽管,这篇文章似乎有些过时,事情本来是可以改变的)。

To answer your second question, I don't think you should be doing a redirect on "page not found" -- one returns a 302, the other should return a 404; one isn't an error condition, the other is. If you lose the status code, your javascript gets more complicated (since you'll have to somehow test the actual returned data, to figure out what happened). These two posts should give you some ideas on how to implement HTTP-friendly error pages. Django does something similar (raise a Http404 exception, return a 404 status code and optionally render a 404.html template).

为了回答你的第二个问题,我认为你不应该在“未找到的页面”上重定向——一个返回302,另一个返回404;一个不是错误条件,另一个是。如果您丢失了状态代码,那么您的javascript将变得更加复杂(因为您将不得不以某种方式测试实际返回的数据,以确定发生了什么)。这两篇文章应该让您了解如何实现http友好的错误页面。Django做了类似的事情(抛出Http404异常,返回404状态代码,并选择性地渲染404)。html模板)。

#4


1  

Action Selector Attribute for Ajax and normal requests

Best way of distinguishing between Ajax requests and normal ones is to write a custom action selector attribute (AjaxOnlyAtribute) and provide two action methods that each handles it's own situation.

区分Ajax请求和普通请求的最佳方法是编写一个自定义操作选择器属性(AjaxOnlyAtribute),并提供两个操作方法,每个方法处理自己的情况。

[HttpPost]
[AjaxOnly]
[ActionName("Add")]
public ActionResult AddAjax(Entity data) { ... }

[HttpPost]
[ActionName("Add")]
public ActionResult AddNormal(Entity data) { ... }

This way you'll avoid code branches and keep code footprint small while also maintaining control over your code. Provide the paired actions only to those actions that need them.

通过这种方式,您将避免代码分支,并使代码占用更少,同时还将维护对代码的控制。只向需要它们的动作提供成对的动作。

Handling validation errors in Ajax requests

Handling validation errors in Ajax calls (or any other errors basically) can be done using exception action filter. I've written a particular one called ModelStateExceptionAttribute. This is the way it's done:

可以使用异常操作过滤器处理Ajax调用中的验证错误(或任何其他错误)。我写了一个特殊的,叫做ModelStateExceptionAttribute。这是它的做法:

[HandleModelStateException]
public ActionResult SomeAjaxAction(Data data)
{
    if (!this.ModelState.IsValid)
    {
        throw new ModelStateException(this.ModelState);
    }
    // usual code from here on
}

And you've probably made your Ajax request using jQuery so such errors gent easily handled by error ajax function:

您可能已经使用jQuery提交了Ajax请求,因此此类错误很容易通过error Ajax函数处理:

$.ajax({
    type: "POST",
    url: "someURL",
    success: function(data, status, xhr) {
        // handle success
    },
    error: function(xhr, status, err) {
        // handle error
    }
});

You can read a detailed blog post about this approach here.

您可以在这里阅读关于此方法的详细博文。

#5


0  

We never redirect (why make the user repeatedly press the 'back' button in case they don't understand what needs to be inputted into a particular field?), we display errors on the spot, be it AJAX or not (the location of the 'spot' on the page is entirely up to you, for all AJAX requests we just show a coloured bar top of the page, just like * does for first-comers, only ours does not push the rest of the content down).

我们从来没有重定向(为什么让用户多次点击“返回”按钮,以防他们不明白需要输入到一个特定的字段?),当场我们显示错误,AJAX与否(页面上的“点”的位置完全取决于你,所有AJAX请求我们只是显示一个彩色栏页面,就像*为把时局造成,只有我们不压低其他内容)。

As for form validation, you can do both server-side and client-side. We always try to show server-side errors on top of the form in a distinctive container, client-side - right next to the field in question on submit. Once the page comes back from the server with server-side validation errors, user will only see the server-side ones initially, so no duplication there.

至于表单验证,您可以同时进行服务器端和客户端验证。我们总是试图在表单的顶部显示服务器端错误,在一个独特的容器中,客户端——就在提交时相关字段的旁边。一旦页面从服务器端返回时出现服务器端验证错误,用户最初只会看到服务器端验证错误,因此不会出现重复。

For data specified in two places, not sure if I understand, since I've never dealt with nHibernate Validation Mappings.

对于在两个地方指定的数据,不确定是否理解,因为我从未处理过nHibernate验证映射。

#6


0  

Keep two error views, one for normal (complete) display of the page and the other which just renders the errors in XML or JSON.

保留两个错误视图,一个用于页面的正常(完整)显示,另一个用于在XML或JSON中呈现错误。

Never validate client-side as the user can easily bypass it. For real-time validation, just run an AJAX request to the server for validation, that way you're writing the validation code once.

永远不要验证客户端,因为用户可以轻松绕过它。对于实时验证,只需运行一个AJAX请求到服务器进行验证,这样就编写了验证代码一次。

#7


0  

I have used xVal in the past, along with some homegrown reflection-based JS rule generators. The idea is that you define the rule once (in your case, via nHibernate), and an HTML helper reflects over your properties and generates client-side validation code on the fly (using the jQuery.validation plugin). Exactly the right way to have a responsive client-side UI, while still enforcing server-side validation.

我过去使用过xVal,以及一些基于反射的JS规则生成器。其思想是,您可以定义一次规则(在您的情况下,通过nHibernate),并且一个HTML助手会在您的属性上反映并生成客户端验证代码(使用jQuery)。验证插件)。拥有响应性客户端UI的正确方法,同时仍然执行服务器端验证。

Unfortunately, this method doesn't work for AJAX-posted forms.

不幸的是,此方法不适用于ajax提交的表单。

For AJAX rules, it would be as simple as adding a Errors array to your returned JSON objects. Anywhere you're using AJAX, simply check the length of Errors (that's all ModelState.IsValid does) and display an error. You can use the IsAjaxRequest method to detect an AJAX call:

对于AJAX规则,只需向返回的JSON对象添加一个错误数组。在使用AJAX的任何地方,只需检查错误的长度(这是所有的ModelState。并显示一个错误。您可以使用IsAjaxRequest方法检测AJAX调用:

public ActionResult PostForm(MyModel thing)
{
  UpdateModel(thing);

  if (this.Request.IsAjaxRequest() == false)
  {
    return View();
  }
  else
  {
    foreach(var error in ModelState.Errors)
    {
      MyJsonObject.Errors.Add(error.Message); 
    }
    return JsonResult(MyJsonObject);
  }
}

#1


3  

The AJAX call is not a page refresh so I would definitely not redirect to a 403, 404 etc. I would display a client side dialog explaining the unexpected result of the request appropriately. Your controller can return the results of failed validation to the AJAX call in a similar fashion to how it would return success data so your dialog can also display whatever is required in this scenario.

AJAX调用不是页面刷新,所以我绝对不会重定向到403、404等。我将显示一个客户端对话框,以适当地解释请求的意外结果。您的控制器可以以类似于返回成功数据的方式将失败验证的结果返回给AJAX调用,这样您的对话框也可以显示此场景所需的任何内容。

#2


2  

Do you have tips on how to make controller actions which work well when called normally and via Ajax? This is a annoying problem when writing action methods.

关于如何使控制器动作在正常调用和通过Ajax调用时运行良好,您有什么建议吗?这是写操作方法时的一个烦人的问题。

Yes, yes I do. We also worked through a similar problem--we wanted the app to have a bunch of forms which would be called generally via ajax but could get hit normally. Moreover, we didn't want a whole bunch of duplicate logic in javascript floating around. Anyhow, the technique we came up with was to use a pair of ActionFilterAttributes to intercept the forms, then a little javascript to wire up the form for ajax handling.

是的,是的,我喜欢。我们还解决了一个类似的问题——我们希望应用程序有一堆表单,这些表单通常通过ajax调用,但通常会受到攻击。此外,我们不希望javascript中出现大量重复的逻辑。总之,我们想到的技术是使用一对ActionFilterAttributes来拦截表单,然后使用一些javascript来连接表单以进行ajax处理。

First, for the ajax request, we just wanted to swap out master pages:

首先,对于ajax请求,我们只想交换母版页:

    private readonly string masterToReplace;

    /// <summary>
    /// Initializes an Ajax Master Page Switcharoo
    /// </summary>
    /// <param name="ajaxMaster">Master page for ajax requests</param>
    public AjaxMasterPageInjectorAttribute(string ajaxMaster)
    {
        this.masterToReplace = ajaxMaster;
    }

    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        if (!filterContext.HttpContext.Request.IsAjaxRequest() || !(filterContext.Result is ViewResult)) return;
        ViewResult vr = (ViewResult) filterContext.Result;
        vr.MasterName = masterToReplace;
    }
}

On the return side, we use xVal to validate client-side, so one shouldn't get much in the way of invalid data, but one can still get some. To that end, we just use normal validation and have the aciton method return the form with validation messages. Successful posts are rewarded with redirects in general. In any case, we do a little json injection for the success case:

在返回端,我们使用xVal来验证客户端,所以不应该有太多无效数据,但是仍然可以得到一些。为此,我们只使用常规验证,并让aciton方法返回带有验证消息的表单。成功的职位通常会得到重定向。在任何情况下,我们都会为成功案例做一些json注入:

/// <summary>
/// Intercepts the response and stuffs in Json commands if the request is ajax and the request returns a RedirectToRoute result.
/// </summary>
public class JsonUpdateInterceptorAttribute : ActionFilterAttribute 
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            JsonResult jr = new JsonResult();
            if (filterContext.Result is RedirectResult)
            {
                RedirectResult rr = (RedirectResult) filterContext.Result;
                jr.Data = new {command = "redirect", content = rr.Url};
            }
            if (filterContext.Result is RedirectToRouteResult)
            {
                RedirectToRouteResult rrr = (RedirectToRouteResult) filterContext.Result;
                VirtualPathData vpd = RouteTable.Routes.GetVirtualPath(filterContext.RequestContext, rrr.RouteValues);
                jr.Data = new {command = "redirect", content = vpd.VirtualPath};
            }
            if (jr.Data != null)
            {
                filterContext.Result = jr;
            }
        }
    }
}

The final trick is using a little javascript object to tie everything together:

最后一个技巧是使用一个小javascript对象将所有的东西连接在一起:

function AjaxFormSwitcher(form, outputTarget, doValidation) {
    this.doValidation = doValidation;
    this.outputTarget = outputTarget;
    this.targetForm = form;
}

AjaxFormSwitcher.prototype.switchFormToAjax = function() {
    var afs = this;
    var opts = {
        beforeSubmit: this.doValidation ? afs.checkValidation : null,
        complete: function(xmlHttp, status){ afs.processResult(afs, xmlHttp, status); },
        clearForm: false
    };
    this.targetForm.ajaxForm(opts);
}

AjaxFormSwitcher.prototype.checkValidation = function(formData, jqForm, options) {
    jqForm.validate();
    return jqForm.valid();
}

AjaxFormSwitcher.prototype.processResult = function(afs, xmlHttp, status) {
    if (xmlHttp == null) return;
    var r = xmlHttp;
    var c = r.getResponseHeader("content-type");
    if (c.match("json") != null) {
        var json = eval("(" + r.responseText + ")");
        afs.processJsonRedirect(json);
    }
    if (c.match("html") != null) {
        afs.outputTarget.html(r.responseText);
    }
}

AjaxFormSwitcher.prototype.processJsonRedirect = function(data) {
    if (data!=null) {
        switch (data.command) {
            case 'redirect':
                window.location.href = data.content;
                break;
        }
    }
}

That little script handles stuff like wiring up the form to do ajax and processing the result (either json command or html which gets displayed in the target). I admittedly suck at writing javascript, so there is probably a much more graceful way to write that.

这个小脚本处理像连接表单这样的东西来执行ajax和处理结果(json命令或html,它们都显示在目标中)。我承认我在编写javascript方面很糟糕,因此可能有一种更优雅的方式来编写它。

#3


1  

Disclaimer: I haven't done much ASP.NET MVC Programming.

免责声明:我没有做太多ASP。净MVC编程。

That said, I've used a number of MVC frameworks in other languages, and have completed quite a few django projects. Over time, I've kinda evolved a pattern to deal with this sort of thing in django, using template inclusion.

也就是说,我使用了许多其他语言的MVC框架,并完成了相当多的django项目。随着时间的推移,我已经演化出一种模式来处理django中的这类问题,使用模板包含。

Essentially, I create a partial template containing the form (and any validation errors), which gets included in the main template. The view (or Controller, in your case) then selects between these two templates: AJAX requests get the partial template, and regular requests get the full template. In the main template, I use the jQuery form plugin to submit the form via AJAX, and simply replace the current form with the data from the server: if validation fails, I get a form with highlighted fields, and a list of errors on top; if the POST succeeds, the form is replaced with a success message, or whatever is more appropriate for your situation.

本质上,我创建了一个包含表单(以及任何验证错误)的部分模板,它包含在主模板中。然后在这两个模板之间选择视图(或控制器):AJAX请求得到部分模板,常规请求得到完整的模板。在主模板中,我使用jQuery表单插件通过AJAX提交表单,并简单地用服务器上的数据替换当前表单:如果验证失败,就会得到一个带有高亮字段的表单,以及顶部出现的错误列表;如果文章成功,表单将被替换为成功消息,或者任何更适合您的情况的消息。

I guess in the ASP.NET MVC world, UserControls could be the equivalent of partials? This article shows how to acheive something similar to what I've described (though, the article seems rather dated, and things could've changed).

我猜是ASP。NET MVC世界,用户控件可以等同于部分吗?本文展示了如何实现与我所描述的类似的东西(尽管,这篇文章似乎有些过时,事情本来是可以改变的)。

To answer your second question, I don't think you should be doing a redirect on "page not found" -- one returns a 302, the other should return a 404; one isn't an error condition, the other is. If you lose the status code, your javascript gets more complicated (since you'll have to somehow test the actual returned data, to figure out what happened). These two posts should give you some ideas on how to implement HTTP-friendly error pages. Django does something similar (raise a Http404 exception, return a 404 status code and optionally render a 404.html template).

为了回答你的第二个问题,我认为你不应该在“未找到的页面”上重定向——一个返回302,另一个返回404;一个不是错误条件,另一个是。如果您丢失了状态代码,那么您的javascript将变得更加复杂(因为您将不得不以某种方式测试实际返回的数据,以确定发生了什么)。这两篇文章应该让您了解如何实现http友好的错误页面。Django做了类似的事情(抛出Http404异常,返回404状态代码,并选择性地渲染404)。html模板)。

#4


1  

Action Selector Attribute for Ajax and normal requests

Best way of distinguishing between Ajax requests and normal ones is to write a custom action selector attribute (AjaxOnlyAtribute) and provide two action methods that each handles it's own situation.

区分Ajax请求和普通请求的最佳方法是编写一个自定义操作选择器属性(AjaxOnlyAtribute),并提供两个操作方法,每个方法处理自己的情况。

[HttpPost]
[AjaxOnly]
[ActionName("Add")]
public ActionResult AddAjax(Entity data) { ... }

[HttpPost]
[ActionName("Add")]
public ActionResult AddNormal(Entity data) { ... }

This way you'll avoid code branches and keep code footprint small while also maintaining control over your code. Provide the paired actions only to those actions that need them.

通过这种方式,您将避免代码分支,并使代码占用更少,同时还将维护对代码的控制。只向需要它们的动作提供成对的动作。

Handling validation errors in Ajax requests

Handling validation errors in Ajax calls (or any other errors basically) can be done using exception action filter. I've written a particular one called ModelStateExceptionAttribute. This is the way it's done:

可以使用异常操作过滤器处理Ajax调用中的验证错误(或任何其他错误)。我写了一个特殊的,叫做ModelStateExceptionAttribute。这是它的做法:

[HandleModelStateException]
public ActionResult SomeAjaxAction(Data data)
{
    if (!this.ModelState.IsValid)
    {
        throw new ModelStateException(this.ModelState);
    }
    // usual code from here on
}

And you've probably made your Ajax request using jQuery so such errors gent easily handled by error ajax function:

您可能已经使用jQuery提交了Ajax请求,因此此类错误很容易通过error Ajax函数处理:

$.ajax({
    type: "POST",
    url: "someURL",
    success: function(data, status, xhr) {
        // handle success
    },
    error: function(xhr, status, err) {
        // handle error
    }
});

You can read a detailed blog post about this approach here.

您可以在这里阅读关于此方法的详细博文。

#5


0  

We never redirect (why make the user repeatedly press the 'back' button in case they don't understand what needs to be inputted into a particular field?), we display errors on the spot, be it AJAX or not (the location of the 'spot' on the page is entirely up to you, for all AJAX requests we just show a coloured bar top of the page, just like * does for first-comers, only ours does not push the rest of the content down).

我们从来没有重定向(为什么让用户多次点击“返回”按钮,以防他们不明白需要输入到一个特定的字段?),当场我们显示错误,AJAX与否(页面上的“点”的位置完全取决于你,所有AJAX请求我们只是显示一个彩色栏页面,就像*为把时局造成,只有我们不压低其他内容)。

As for form validation, you can do both server-side and client-side. We always try to show server-side errors on top of the form in a distinctive container, client-side - right next to the field in question on submit. Once the page comes back from the server with server-side validation errors, user will only see the server-side ones initially, so no duplication there.

至于表单验证,您可以同时进行服务器端和客户端验证。我们总是试图在表单的顶部显示服务器端错误,在一个独特的容器中,客户端——就在提交时相关字段的旁边。一旦页面从服务器端返回时出现服务器端验证错误,用户最初只会看到服务器端验证错误,因此不会出现重复。

For data specified in two places, not sure if I understand, since I've never dealt with nHibernate Validation Mappings.

对于在两个地方指定的数据,不确定是否理解,因为我从未处理过nHibernate验证映射。

#6


0  

Keep two error views, one for normal (complete) display of the page and the other which just renders the errors in XML or JSON.

保留两个错误视图,一个用于页面的正常(完整)显示,另一个用于在XML或JSON中呈现错误。

Never validate client-side as the user can easily bypass it. For real-time validation, just run an AJAX request to the server for validation, that way you're writing the validation code once.

永远不要验证客户端,因为用户可以轻松绕过它。对于实时验证,只需运行一个AJAX请求到服务器进行验证,这样就编写了验证代码一次。

#7


0  

I have used xVal in the past, along with some homegrown reflection-based JS rule generators. The idea is that you define the rule once (in your case, via nHibernate), and an HTML helper reflects over your properties and generates client-side validation code on the fly (using the jQuery.validation plugin). Exactly the right way to have a responsive client-side UI, while still enforcing server-side validation.

我过去使用过xVal,以及一些基于反射的JS规则生成器。其思想是,您可以定义一次规则(在您的情况下,通过nHibernate),并且一个HTML助手会在您的属性上反映并生成客户端验证代码(使用jQuery)。验证插件)。拥有响应性客户端UI的正确方法,同时仍然执行服务器端验证。

Unfortunately, this method doesn't work for AJAX-posted forms.

不幸的是,此方法不适用于ajax提交的表单。

For AJAX rules, it would be as simple as adding a Errors array to your returned JSON objects. Anywhere you're using AJAX, simply check the length of Errors (that's all ModelState.IsValid does) and display an error. You can use the IsAjaxRequest method to detect an AJAX call:

对于AJAX规则,只需向返回的JSON对象添加一个错误数组。在使用AJAX的任何地方,只需检查错误的长度(这是所有的ModelState。并显示一个错误。您可以使用IsAjaxRequest方法检测AJAX调用:

public ActionResult PostForm(MyModel thing)
{
  UpdateModel(thing);

  if (this.Request.IsAjaxRequest() == false)
  {
    return View();
  }
  else
  {
    foreach(var error in ModelState.Errors)
    {
      MyJsonObject.Errors.Add(error.Message); 
    }
    return JsonResult(MyJsonObject);
  }
}