有没有办法让Ajax.BeginForm返回JSON并自动更新Form

时间:2011-02-19 09:29:01

标签: asp.net-mvc ajax json asp.net-mvc-3

我想要做的是使用DataAnnotation来识别模型视图上的验证。我还希望能够使用Ajax.BeginForm或其他功能调用一个动作,该动作返回会自动更新表单的JSON。

我编写了代码,我使用JQuery功能$ .Ajax向服务器提交表单的数据。该操作返回一个模型视图,其中包含错误和转换为JSON的其他信息。但是在客户端我不得不编写自定义JavaScript来显示错误等。这似乎过于复杂。似乎应该有更好的方法来做到这一点。我错过了什么

是否有人知道这样做的更好方法,或者知道可以指向正确方向的任何资源?

BarDev

2 个答案:

答案 0 :(得分:4)

我一直在解决这个问题,并认为我已经找到了一个不错的解决方案。这是我的人为的业务要求。创建一个在提交表单时不刷新的登录屏幕。如果用户未成功登录,则向用户显示消息。必须输入用户名和密码。用户名不能包含任何空格。如果用户未输入用户名或密码显示错误。

以下是一些技术要求。验证应该在模型中使用数据注释。验证应该是DRY(不要重复自己)。客户端验证应在提交数据之前验证UserName和Password。应将Json返回给客户端,并在服务器上发现任何错误。

以下是模型。第一个是LoginModelView。它有两个属性:UserName和Password。这两个属性都具有数据注释属性。这些属性将用于客户端验证和服务器端验证。 ErrorModelView将用于返回登录结果以及可能已识别的任何错误。

using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;

namespace TestClientSideValidation.Models
{
    public class LoginModelView
    {
        [Required(ErrorMessage = "UserName Is Required")]
        [RegularExpression(@"(\S)+", ErrorMessage = "White space is not allowed")]
        [StringLength(12, MinimumLength = 3)]
        public string UserName { get; set; }

        [Required(ErrorMessage = "Password Is Required")]
        [StringLength(20, MinimumLength = 3)]
        public string Password { get; set; }

        public bool RememberMe { get; set; }
    }

    public class ErrorModelView
    {
        public bool IsLogInSuccessful { get; set; }
        public List<Error> Errors { get; set; }
    }

    public class Error
    {
        public string Key { get; set; }
        public List<string> Messages { get; set; }
    }
}

AccountServices类模拟针对数据存储检查Login Credential。首先,我们检查模型状态当前是否有效。如果它无效,则没有理由访问数据存储。如果模型状态有效,则使用数据存储验证登录凭据。如果登录凭据无效,则向ModelState添加错误。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using TestClientSideValidation.Models;

namespace TestClientSideValidation.Services
{
    public static class AccountServices
    {
        public static void ValidateLogin(ModelStateDictionary modelState, LoginModelView loginModelView)
        {
            if (modelState.IsValid) //If modelState is Invalid there is not reason tho check the login is valid
            {
                if (loginModelView.UserName != "Admin" || loginModelView.Password != "Password")
                {
                    modelState.AddModelError("_FORM", "The username or password provided is incorrect.");
                }
            }
        }

    }
}

控制器包含两个操作:Login和JsonLogin。 Login操作非常简单;创建一个空的LoginViewModel并将其传递给Login视图。

LoginJson完成了我们在此应用程序中的大部分工作。最初,传入的LoginModelView是根据我们之前创建的AccountSerices.Validation进行验证的。然后检查ModelState。如果模式状态无效,则将数据从ModelState移动到ErrorModelView。请原谅我的LINQ,我知道它非常难看。也许我会重构这个或其他一些可以。此外,ModelViewError.IsLogInSuccessful设置为false。然后,我们使用Json方法发送回模型视图,以将数据序列化为Json。

如果一切都有效,则创建FormsAuthenticationTicket,将ErrorModelView.IsLogInSuccessful设置为true并将Json返回给客户端。

using System.Web;
using System.Web.Mvc;
using TestClientSideValidation.Models;
using TestClientSideValidation.Services;
using System.Web.Security;
using System;
using System.Linq;

namespace TestClientSideValidation.Controllers
{
    public class AccountController : Controller
    {
        public ActionResult Login()
        {
            LoginModelView loginModelView = new LoginModelView();
            return View(loginModelView);
        }

        [System.Web.Mvc.OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
        public virtual JsonResult LoginJson(LoginModelView loginModelView)//Share Model

        {
            ErrorModelView modelView = new ErrorModelView();
            AccountServices.ValidateLogin(ModelState, loginModelView);
            if (!ModelState.IsValid)
            {
                modelView.Errors = (from d in
                                  (from ms in ModelState
                                   where ms.Value.Errors.Count > 0
                                   select ms
                                      ).ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Errors.Select(e => e.ErrorMessage).ToArray())
                              select new Error
                              {
                                  Key = d.Key,
                                  Messages = d.Value.ToList()
                              }).ToList();


                modelView.IsLogInSuccessful = false;
                return Json(modelView, JsonRequestBehavior.AllowGet);
            }

            FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket
            (
                1,
                loginModelView.UserName,
                DateTime.Now,
                DateTime.Now.AddDays(30),
                loginModelView.RememberMe,
                loginModelView.UserName.ToString()
            );
            string encTicket = FormsAuthentication.Encrypt(authTicket);
            this.Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));
            modelView.IsLogInSuccessful = true;
            return Json(modelView, JsonRequestBehavior.AllowGet);
        }
    }
}

此示例的最后一部分显示视图并接受来自操作控制器的返回JSON。第一行标识LoginModelView的强类型模型。此模式是我们之前创建的模式,包括UserName和Passwords and Validation属性。要使验证和Ajax调用起作用,我们需要包含一些JavaScript文件。如果你跳到这个代码块的底部,你会注意到我们在提交页面时调用了Ajax.BeginForm。这将调用LoginJson操作并传递表单中的值。此外,我在此代码块中先前声明的以下JavaScript函数也将在该过程中的某些点触发;只有OnSuccess有代码。在这个例子中,@ Html.TextBoxFor和@ Html.ValidationMessageFor做了一些魔术。这是客户端验证的用武之地。

当从LoginJson操作中重新调用Json时,将触发JavaScript函数OnSucces。如果有错误,则调用DisplayErrors函数并循环错误并将它们添加到Errors Div。如果没有错误,则提醒用户登录成功。

@model TestClientSideValidation.Models.LoginModelView

<html>
<head>
    <title>Login</title>

    <script src="/Scripts/jquery-1.4.4.min.js" type="text/javascript"></script>
    @*This is need for validation*@
    <script src="/Scripts/jquery.validate.js" type="text/javascript"></script>
    <script src="/Scripts/jquery.validate.unobtrusive.js" type="text/javascript"></script>  
    @*This is need for validation Ajax.BeginForm*@
    <script src="/Scripts/jquery.unobtrusive-ajax.js" type="text/javascript"></script>

    <script type="text/javascript">
        function OnSuccess(e) {
            if (e.IsLogInSuccessful) {
                ClearErrors();
               alert("You logged in");
            }
            else {
                ClearErrors();
                DisplayErrors(e.Errors);
            }
        }

        function OnFailure(e) {
        }

        function OnComplete(e) {
        }

        function DisplayErrors(errors) {
            for (error in errors) {
                for (message in errors[error].Messages) {
                    if (message != null && message != "") {
                        $("#Errors").append(errors[error].Messages[message]);
                    }
                }
            }
        }

        function ClearErrors() {
            $("#Errors").text("");
        }
    </script>

</head>
<body>
    <div>
    <div id="Errors"></div>
    @using (Ajax.BeginForm("LoginJson", new AjaxOptions { HttpMethod = "Post", OnFailure = "OnFailure", OnSuccess = "OnSuccess", OnComplete = "OnComplete" }))
    {
        <div>
            @Html.LabelFor(m => m.UserName)
            @Html.TextBoxFor(m => m.UserName)
            @Html.ValidationMessageFor(m => m.UserName)
        </div>
        <div>
            @Html.LabelFor(m => m.Password)
            @Html.TextBoxFor(m => m.Password)
            @Html.ValidationMessageFor(m => m.Password)
        </div>
        <input type="submit" id="loginDialog_submitForm" value="Submit Form" />
    }
</body>
</html>

首先,抱歉扩展的例子。但希望这会帮助一些人。或者,如果有更好或不同的方式,有些人会回复。

因此,这解决了所有验证规则都在一个地方的主要问题。我也相信我们也满足了人为的业务/技术要求。

我试图让这很简单,并且有很多方法可以改善这一点,但我相信这是一个很好的开始。我目前在JQuery UI中使用它,通过使视图成为局部视图并在模式对话框中显示视图。

再次,我希望这可以帮助人们。


更新

确保以下内容位于web.config

  <appSettings>
    <add key="ClientValidationEnabled" value="true"/> 
    <add key="UnobtrusiveJavaScriptEnabled" value="true"/> 
  </appSettings>

BarDev

答案 1 :(得分:2)

不,你没有遗漏任何东西。如果您返回JSON,则需要手动在客户端上处理它以更新GUI。一个更简单的解决方案是返回包含包含所有验证错误的表单html的部分视图。

相关问题