使用.Net MVC 3在ViewModel中验证当前密码以更改密码的最佳做法?

使用.Net MVC 3在ViewModel中验证当前密码以更改密码的最佳做法?,.net,asp.net-mvc,asp.net-mvc-3,data-annotations,.net,Asp.net Mvc,Asp.net Mvc 3,Data Annotations,我的ViewModel: public class EditViewModel { [Required] public string CurrentPassword { get; set; } public string NewPassword { get; set; } [Compare("NewPassword")] public string ConfirmNewPassword { get; set; } } public class Edi

我的ViewModel:

public class EditViewModel
{
    [Required]
    public string CurrentPassword { get; set; }

    public string NewPassword { get; set; }

    [Compare("NewPassword")]
    public string ConfirmNewPassword { get; set; }
}
public class EditViewModel
{
    [CurrentPassword]
    public string CurrentPassword { get; set; }

    public string NewPassword { get; set; }

    [Compare("NewPassword")]
    public string ConfirmNewPassword { get; set; }
}
我正在尝试验证CurrentPassword字段。我考虑创建一个自定义验证器属性,该属性连接到我的数据库(用于检索当前密码),以便与DataAnnotation一起使用

有更好的主意吗

更新:


我知道我可以在我的控制器中进行验证,但我试图在我的控制器中避免这种验证逻辑。只是为了我的代码/架构之美

我还没有见过比创建一个用于DataAnnotation的自定义验证器更好的解决方案,您认为呢?

您可以使用 [远程]注释。

它将对某个控制器中的方法进行ajax调用。在那里,您可以访问数据库或执行一些其他验证

[Remote("CheckPassword","YourController", ErrorMessage = "Wrong currentpassword")]
[Required]
public string CurrentPassword { get; set; }


public ActionResult CheckPassword(String Password)
        {
            if (YourRepository.CheckIfPassIsCorrect(Password))
            {
                return Json(false, JsonRequestBehavior.AllowGet);
            }
            else
            {
                return Json(true, JsonRequestBehavior.AllowGet);
            }
        }
另一种解决方案

当您提交给控制器时。 这样做:

  if(!YourRepository.CheckIfPassIsCorrect(YourViewModel.CurrentPassword)
    {
    ModelState.AddModelError("CurrentPassword", "Your current password isn't correct");

//Return to your view
    }

为什么不直接打电话给
会员。更改密码
?如果失败,则可以向ModelState添加错误:

    [Authorize]
    [HttpPost]
    public ActionResult ChangePassword(ChangePasswordModel model)
    {
        if (ModelState.IsValid)
        {

            // ChangePassword will throw an exception rather
            // than return false in certain failure scenarios.
            bool changePasswordSucceeded;
            try
            {
                MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */);
                changePasswordSucceeded = currentUser.ChangePassword(model.OldPassword, model.NewPassword);
            }
            catch (Exception)
            {
                changePasswordSucceeded = false;
            }

            if (changePasswordSucceeded)
            {
                return RedirectToAction("ChangePasswordSuccess");
            }
            else
            {
                ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
            }
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

顺便说一下,这都是在默认的Internet项目模板中完成的。除非您需要客户端验证,否则我不确定检查密码是否正确,然后调用ChangePassword的目的是什么。这只是对数据库的额外调用,似乎不必要。

我的解决方案是构建一个自定义验证,用于DataAnnotation,下面是代码:

public class CurrentPasswordAttribute : ValidationAttribute
{
    AuthenticationService authenticationService = new AuthenticationService();

    public override bool IsValid(object value)
    {
        string strValue = value as string;

        if (!string.IsNullOrEmpty(strValue))
        {
            return authenticationService.ValidateUser(HttpContext.Current.User.Identity.Name, strValue);
        }

        return false;
    }

    public override string FormatErrorMessage(string name)
    {
        return String.Format(CultureInfo.CurrentCulture, base.ErrorMessageString, name);
    }
}
在我的ViewModel中:

public class EditViewModel
{
    [Required]
    public string CurrentPassword { get; set; }

    public string NewPassword { get; set; }

    [Compare("NewPassword")]
    public string ConfirmNewPassword { get; set; }
}
public class EditViewModel
{
    [CurrentPassword]
    public string CurrentPassword { get; set; }

    public string NewPassword { get; set; }

    [Compare("NewPassword")]
    public string ConfirmNewPassword { get; set; }
}

谢谢。

我不想为此使用Javascript。我不知道RemoteAttribute。伟大的为什么要将密码验证为电子邮件地址?添加了另一个解决方案,哈哈,电子邮件内容是一个打字错误,因为我从我的一个型号复制了远程注释;)我来编辑it@Kevin我试图在控制器中避免这种验证逻辑。只是为了我的代码/体系结构的美观。我想定制一个验证器是一个不错的选择,然后我试图在我的控制器中避免这种验证逻辑。只是为了我的代码/体系结构的美观。@Acaz,然后重构此解决方案,以便在服务中进行所有操作?@Tomas是的,使用带有数据批注的自定义验证器。根据您的编辑,如果您不想在控制器中进行验证,您想在哪里进行验证?如果您想在客户端执行此操作,则必须将密码(或某种形式的密码)发送到客户端。。。这是我建议你避免的一种做法。任何能让你的船漂浮的东西——但我个人不同意这一点。属性不应调用到服务中。@RPM1984您建议什么解决方案?它属于控制器。你说你不想这样做是为了“我的代码/架构之美”。但控制器的任务是处理模型,并更新视图。调用外部服务当然不是自定义属性作业。首先,它使单元测试变得非常困难。基本上,@Kevin Cloet的第二个答案是正确的。我很想知道这是如何影响您的体系结构的?因此,如果您的控制器太“胖”,那么就将代码抽象到服务中。比精简控制器更大的最佳实践是分离关注点。同一类中的属性和服务不是分离的。“查看密码是否正确”是一个域/身份验证问题,它不是真正的输入验证,这就是属性的用途。因此,我建议将“fat代码”放在服务中,并让控制器调用该代码。@RPM1984是的,我同意你的意见,但当前密码是一个我仅在视图中看到的字段,而不存在于域中。因此,输入验证是当前密码中唯一进行验证的地方,不是吗?