【问题标题】:How to read modelstate errors when returned by Json?Json返回时如何读取模型状态错误?
【发布时间】:2011-02-17 23:54:36
【问题描述】:

如何显示 JSON 返回的 ModelState 错误?

我想做这样的事情:

 if (!ValidateLogOn(Name, currentPassword))
    {
        ModelState.AddModelError("_FORM", "Username or password is incorrect.");

        //Return a json object to the javascript
        return Json(new { ModelState });
    }

我在视图中的代码必须是什么才能读取 ModelState 错误并显示它们?

我在视图中读取 JSON 值的实际代码如下:

function createCategoryComplete(e) { 
    var obj = e.get_object(); 
    alert(obj.Values); 
} 

【问题讨论】:

标签: asp.net-mvc error-handling modelstate


【解决方案1】:

如果要返回 JSON,则不能使用 ModelState。视图需要的所有内容都应该包含在 JSON 字符串中。因此,您可以将其添加到您正在序列化的模型中,而不是将错误添加到 ModelState 中:

public ActionResult Index()
{
    return Json(new 
    {
        errorControl = "_FORM",
        errorMessage = "Username or password is incorrect.",
        someOtherProperty = "some other value"
    });
}

【讨论】:

  • 不明白为什么不能使用ModelState。例如。模型绑定器可以将错误放入模型状态、动作过滤器等​​;如果我们不使用 ModelState,用户甚至不会知道它们。
  • ModelState 不序列化。我收到了循环引用错误。
【解决方案2】:

这是草稿代码,但同样的想法在生产中也适用于我。 这里的主要思想是 Json 错误具有预定义的标签名称,普通对象不会有。对于错误验证错误,使用 JavaScript 重新创建 HTML(顶部摘要和表单元素突出显示)。

服务器端:

  public static JsonResult JsonValidation(this ModelStateDictionary state)
  {
     return new JsonResult
     {
        Data = new
           {
              Tag = "ValidationError",
              State = from e in state
                      where e.Value.Errors.Count > 0
                      select new
                      {
                         Name = e.Key,
                         Errors = e.Value.Errors.Select(x => x.ErrorMessage)
                            .Concat(e.Value.Errors.Where(x => x.Exception != null).Select(x => x.Exception.Message))
                      }
           }
     };
  }

  in action:
  if (!ModelState.IsValid && Request.IsAjaxRequest())
      return ModelState.JsonValidation();

客户端:

function getValidationSummary() {
   var el = $(".validation-summary-errors");
   if (el.length == 0) {
      $(".title-separator").after("<div><ul class='validation-summary-errors ui-state-error'></ul></div>");
      el = $(".validation-summary-errors");
   }
   return el;
}

function getResponseValidationObject(response) {
   if (response && response.Tag && response.Tag == "ValidationError")
      return response;
   return null;
}

function CheckValidationErrorResponse(response, form, summaryElement) {
   var data = getResponseValidationObject(response);
   if (!data) return;

   var list = summaryElement || getValidationSummary();
   list.html('');
   $.each(data.State, function(i, item) {
      list.append("<li>" + item.Errors.join("</li><li>") + "</li>");
      if (form && item.Name.length > 0)
         $(form).find("*[name='" + item.Name + "']").addClass("ui-state-error");
   });
}

$.ajax(... function(response) { 
   CheckValidationErrorResponse(xhr.responseText); } );

【讨论】:

  • 干得好!您提供的代码也非常可移植。我唯一需要更改的是 "$(".title-separator")" 以匹配我的 DOM。
  • 你设置什么:summaryElement ??我在您的代码中的任何地方都没有看到。
  • 如果我想在非默认位置显示错误,summaryElement 将传递给 CheckValidationErrorResponse - 例如在 jqGrid 编辑表单中。这是 ul/div/etc 的 jquery 元素/选择器,其内容将替换为带有错误的 li 标签。在我上面的代码中,您将在 $.ajax 回调中传递 CheckValidationErrorResponse。
【解决方案3】:

这是对处理特定验证消息的queen3客户端代码的微小调整,并创建与MVC3创建的文档类似的文档:

function getValidationSummary() {
   var $el = $(".validation-summary-errors > ul");
   if ($el.length == 0) {
       $el = $("<div class='validation-summary-errors'><ul></ul></div>")
                .hide()
                .insertBefore('fieldset:first')
                .find('ul');
   } 
   return $el;
}
function getResponseValidationObject(response) {
   if (response && response.Tag && response.Tag == "ValidationError")
      return response;
   return null;
}
function isValidationErrorResponse(response, form, summaryElement) {
    var $list,
        data = getResponseValidationObject(response);
    if (!data) return false;
    $list = summaryElement || getValidationSummary();
    $list.html('');
    $.each(data.State, function (i, item) {
        var $val, lblTxt, errorList ="";
        if (item.Name) {
            $val = $(".field-validation-valid,.field-validation-error")
                        .first("[data-valmsg-for=" + item.Name + "]")
                        .removeClass("field-validation-valid")
                        .addClass("field-validation-error");
            $("input[name=" + item.Name + "]").addClass("input-validation-error")
            lblTxt = $("label[for=" + item.Name + "]").text();
            if (lblTxt) { lblTxt += ": "; }
        }
        if ($val.length) {
            $val.text(item.Errors.shift());
            if (!item.Errors.length) { return; }
        }
        $.each(item.Errors, function (c,val) {
            errorList += "<li>" + lblTxt + val + "</li>";
        });
        $list.append(errorList);
    });
    if ($list.find("li:first").length) {$list.closest("div").show(); }
    return true;
}

【讨论】:

  • 谢谢布伦特。几个建议 - $.first() 不接受参数,应该是 $.filter() 吗?然后 $("input[name=...]") 排除 select、textarea 等 - 可以更改为 $("*[name=...]")?
【解决方案4】:

请参阅下面的代码,其中对 Brent 的答案进行了一些修改。 CheckValidationErrorResponse 查找验证摘要,无论它是处于有效还是无效状态,如果未找到则插入它。如果在响应中发现验证错误,它将验证摘要错误类应用于摘要,否则应用验证摘要有效。它假定存在 CSS 来控制摘要的可见性。

代码会清除 field-validation-error 的现有实例,并针对响应中发现的错误重新应用它们。

function getValidationSummary(form) {
    var $summ = $(form).find('*[data-valmsg-summary="true"]');

    if ($summ.length == 0)
    {
    $summ = $('<div class="validation-summary-valid" data-valmsg-summary="true"><ul></ul></div>');
        $summ.appendTo(form);
    }

    return $summ;
}

function getValidationList(summary) {
    var $list = $(summary).children('ul');

    if ($list.length == 0) {
        $list = $('<ul></ul>');
        $list.appendTo(summary);
    }

    return $list;
}

function getResponseValidationErrors(data) {
    if (data && data.ModelErrors && data.ModelErrors.length > 0)
        return data.ModelErrors;
    return null;
}

function CheckValidationErrorResponse(data, form, summaryElement) {
    var errors = getResponseValidationErrors(data);
    var $summ = summaryElement || getValidationSummary(form);
    var $list = getValidationList($summ);

    $list.html('');

    $(form).find(".field-validation-error")
           .removeClass("field-validation-error")
           .addClass("field-validation-valid");

    if (!errors)
    {
        $summ.removeClass('validation-summary-errors').addClass('validation-summary-valid');
        return false;
    }

    $.each(errors, function (i, item) {
        var $val, $input, errorList = "";
        if (item.Name) {
            $val = $(form).find(".field-validation-valid, .field-validation-error")
                          .filter("[data-valmsg-for=" + item.Name + "]")
                          .removeClass("field-validation-valid")
                          .addClass("field-validation-error");

            $input = $(form).find("*[name='" + item.Name + "']");

            if (!$input.is(":hidden") && !$val.length)
            {
                $input.parent().append("<span class='field-validation-error' data-valmsg-for='" + item.Name + "' data-valmsg-replace='false'>*</span>");
            }

            $input.addClass("input-validation-error");
        }

        $.each(item.Errors, function (c, err) {
            errorList += "<li>" + err + "</li>";
        });

        $list.append(errorList);
    });

    $summ.removeClass('validation-summary-valid').addClass('validation-summary-errors');
    return true;
}

【讨论】:

    【解决方案5】:

    为什么不将原始的ModelState对象返回给客户端,然后使用jQuery读取值。对我来说它看起来更简单,并且使用通用数据结构(.net 的ModelState

    C#:

    return Json(ModelState);
    

    js:

    var message = "";
    if (e.response.length > 0) {
        $.each(e.response, function(i, fieldItem) {
            $.each(fieldItem.Value.Errors, function(j, errItem) {
                message += errItem.ErrorMessage;
            });
            message += "\n";
        });
        alert(message);
    }
    

    【讨论】:

      【解决方案6】:

      C#

       public class ValidateModelAttribute : ActionFilterAttribute
          {
              public override void OnActionExecuting(HttpActionContext actionContext)
              {
                  if (actionContext.ModelState.IsValid == false)
                  {
                      actionContext.Response = actionContext.Request.CreateErrorResponse(
                          HttpStatusCode.BadRequest, actionContext.ModelState);
                  }
              }
          }
      

      JavaScript

      $.ajax({
              type: "GET",
              url: "/api/xxxxx",
              async: 'false',
              error: function (xhr, status, err) {
                  if (xhr.status == 400) {
                      DisplayModelStateErrors(xhr.responseJSON.ModelState);
                  }
              },
      ....
      
      
      function DisplayModelStateErrors(modelState) {
          var message = "";
          var propStrings = Object.keys(modelState);
      
          $.each(propStrings, function (i, propString) {
              var propErrors = modelState[propString];
              $.each(propErrors, function (j, propError) {
                  message += propError;
              });
              message += "\n";
          });
      
          alert(message);
      };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-12-06
        • 1970-01-01
        • 2012-07-07
        • 2016-05-07
        • 2023-03-18
        • 1970-01-01
        • 2015-10-13
        相关资源
        最近更新 更多