绕过控制器中的模型验证
本文关键字:模型 验证 控制器 | 更新日期: 2023-09-27 18:34:38
我已经为我的数据库创建了一个 ADO.NET 模型。使用 CRUD(实体框架并使用我创建的 ADO.NET 实体模型(创建了一个新控制器。
在我的数据库中,我有一个简单的用户表。表中的"密码"行将保存使用简单加密(PBKDF2(加密的用户密码。
在我的 ADO.NET 用户.cs模型中,我添加了以下验证:
[Required]
[DataType(DataType.Password)]
[StringLength(20, MinimumLength = 6)]
[Display(Name = "Password")]
public string Password { get; set; }
这适用于浏览器中的jQuery和验证。但是在我的控制器中,我正在加密密码,然后密码字符串的长度将超过 20 个字符。
var crypto = new SimpleCrypto.PBKDF2();
var encryptedPass = crypto.Compute(user.Password);
user.Password = encryptedPass;
user.PasswordSalt = crypto.Salt;
_db.Users.Add(user);
_db.SaveChanges();
这给了我"一个或多个实体的验证失败"。-错误。
我可以将用户复制到"var newUser",然后在那里设置所有属性,但在这种情况下,没有更简单的方法可以绕过模型验证吗?
编辑:如果我删除模型中密码属性的验证,那么一切正常。因此,正是验证给了我错误,因为由于控制器中的加密,我将密码从 6-20 长度字符更改为 +100 长度字符。
编辑:插入此问题的完整控制器部分。
[HttpPost]
public ActionResult Create(Users user)
{
if (!ModelState.IsValid)
{
return View();
}
if (_db.Users.FirstOrDefault(u => u.Email == user.Email) != null)
{
ModelState.AddModelError("", "User already exists in database!");
return View();
}
var crypto = new SimpleCrypto.PBKDF2();
var encryptedPass = crypto.Compute(user.Password);
user.Password = encryptedPass;
user.PasswordSalt = crypto.Salt;
_db.Users.Add(user);
_db.SaveChanges();
return RedirectToAction("Index", "User");
}
这就是ViewModels进入游戏的地方。您应该创建一个传递给视图的模型,并在以后将其映射回域模型。
视图模型:
public class RegisterModel
{
[Required]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[StringLength(20, MinimumLength = 6)]
[Display(Name = "Password")]
public string Password { get; set; }
}
域模型(当前User
模型(:
public class User
{
// other properties..
[Required]
public string Password { get; set; }
}
您可以在控制器中使用这些模型,如下所示:
GET
操作:
public ActionResult Register()
{
var registerModel = new RegisterModel();
return View(registerModel)
}
具有这样的视图:
@model RegisterModel
@Html.LabelFor(model => model.UserName)
@Html.TextBoxFor(model => model.UserName)
@Html.ValidationMessageFor(model => model.UserName)
@Html.LabelFor(model => model.Password)
@Html.PasswordFor(model => model.Password)
@Html.ValidationMessageFor(model => model.Password)
POST
动作:
[HttpPost]
public ActionResult Register(RegisterModel registerModel)
{
// Map RegisterModel to a User model.
var user = new User
{
UserName = registerModel.UserName,
Password = registerModel.Password // Do the hasing here for example.
};
db.Users.Add(user);
db.SaveChanges();
}
您说您的密码加密发生在控制器中。 在这种情况下,您不应该在验证后加密吗? 例如:
public ActionResult SomeControllerAction(UserViewModel user)
{
if (!ModelState.IsValid)
{
// at this point the human readable (and assuming < 20 length) password
// would be getting validated
return View(user);
}
// now when you're writing the record to the DB, encrypt the password
var crypto = new SimpleCrypto.PBKDF2();
var encryptedPass = crypto.Compute(user.Password);
user.Password = encryptedPass;
user.PasswordSalt = crypto.Salt;
_db.Users.Add(user);
_db.SaveChanges();
// return or redirect to whatever route you need
}
如果要专门控制验证,请尝试在视图模型类上实现IValidatableObject
并在此处执行验证,而不是通过属性。 例如:
public class UserViewModel : IValidatableObject
{
public string Username { get; set; }
public string Password { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// validate the unencrypted password's length to be < 20
if (this.Password.Length > 20)
{
yield return new ValidationResult("Password too long!");
}
}
}
如果我理解正确,您有一个带有密码字段的数据库表。
根据您的型号,此密码字段的长度
为 20 个字符
[StringLength(20, MinimumLength = 6)]
并且您要插入一个大于 20 个字符的值。
如果实体框架没有阻止您,则会收到数据库错误。(实体框架不知道数据模型和数据库之间存在不匹配,并且不想冒着推动插入的风险(
但是,我假设您要在视图模型上指定客户端验证规则,而不是在数据库上指定长度约束。
我希望你明白为什么这是一个令人困惑的设置。
我的建议是拆分您的视图模型和模型,以便您可以发布具有最大长度为 20 的未加密密码的 viewModel,您可以将其转换为长度为 100 的模型密码。
如果您发现这太麻烦,您可以创建一个未映射的密码属性,当您发布它并将其转换为控制器中的密码属性时,您可以从 html 设置该属性。
你的类可能看起来像这样:
public class RegisterModel
{
[Required]
public string UserName { get; set; }
[Required]
[NotMapped]
[StringLength(20, MinimumLength = 6)]
[Display(Name = "Password")]
public string PlainTextPassword { get; set; }
[Required]
[StringLength(300)]//This is optional
[DataType(DataType.Password)]
public string Password { get; set; }
}