在 Asp.Net MVC 中禁用模型验证
Posted
技术标签:
【中文标题】在 Asp.Net MVC 中禁用模型验证【英文标题】:Disable Model Validation in Asp.Net MVC 【发布时间】:2011-04-04 11:23:11 【问题描述】:如何禁用 Controller 中单个 Action 的模型验证? 或者我可以通过在某处启动时注册模型类型来为每个模型执行此操作吗?
我希望 ModelBinder 绑定到模型,但之后它不应该执行模型验证。
我不想进行验证的原因是因为我试图将逻辑从控制器移动到服务层,该服务层将负责验证模型,因为我不能假设传递给服务的模型包含有效数据.
据我了解,这是推荐的方法(控制器中没有逻辑),所以我觉得有点奇怪,我似乎找不到任何关于如何禁用模型验证的信息(每个操作或每个模型类型) )。
请注意,我不想禁用整个 web 应用程序的模型验证(通过删除验证提供程序),并且我不想禁用检查是否发布恶意代码的输入验证。
更新
我正在使用 .Net 4.0 和 MVC 3 Preview 1
【问题讨论】:
【参考方案1】:我用 [验证输入(假)]
不确定这是否会阻止模型验证,或者只有 IIS 提交验证。
【讨论】:
它不会阻止模型被验证。据我所知,它用于禁用对恶意输入的检查(如 html 标签或 javascript 等)【参考方案2】:我建议您在两个 位置执行验证,而不是尝试在 UI 中关闭验证。我理解您的观点,即服务不能假定它正在传递有效数据 - 这是一个有效的问题,也是您的服务应该进行验证的原因。但是您应该也在您的 UI 中进行验证。这也很好,因为您可以进行客户端验证以防止用户错误并为您提供更好的用户体验。
【讨论】:
感谢您的回复。我有客户端 UI 验证。我将 DataAnnotations 命名空间中的属性用于我的模型。我的服务层将验证错误添加到 Modelstate(如果在 Web 上下文中)。没有理由两次执行相同的验证。 :) 基本上我的服务层遵循这种方法asp.net/mvc/tutorials/validating-with-a-service-layer--cs 它工作正常,但该示例不使用 DataAnnotations 属性进行验证,这些属性在 MVC 中自动验证(以及我试图阻止的)。 该链接没有关于何时编写的日期,但我猜它已经很老了。当 MVC 首次发布时,对于验证逻辑应该去哪里(控制器等) 存在一些混淆。但是这个故事在去年左右得到了巩固,推荐的最佳实践通常是 UI 验证应该在模型绑定器中进行,这就是为什么现在这成为 MVC 2 中的默认设置。 如果您打算这样做,那么可以通过继承 DataAnnotationModelBinder 然后重写 OnModelUpdated() 方法并在内部执行 ModelState.Clear() 来完成。我并不是说我会这样做。 :) 我假设您在谈论 DefaultModelBinder(因为没有 DataAnnotationsModelBinder - 虽然有一个 DataAnnotationsModelValidator)。在 ModelBinder 中执行 ModelState.Clear() 会影响我不想要的整个 WebApplication,我希望它继续以传统方式工作,但能够禁用每个操作或每个模型类型的模型验证。我当然可以进行大切换或 if 子句并添加我不想验证的每个模型类型 - 但我猜这会很丑 - 但也许这是最好的选择! :)【参考方案3】:不幸的是,似乎没有简单的方法可以禁用 ModelBinder 中发生的模型验证,除非将您不想验证的每个模型类型(包括嵌套的复杂类型)注册到特定的 ModelBinder。可以通过以下代码完成:
ModelBinders.Binders[typeof(MyModelType)] = new NonValidatingModelBinder();
创建一个可以附加到操作方法或操作方法参数的 SkipValidationAttribute 似乎是不可能的,因为它应该在 ControllerActionInvoker 中实现,但是没有办法让 ModelBinder 知道它应该在 SetProperty 中进行任何验证() 和 OnModelUpdated 方法在 GetParameterValue() 方法中调用 BindModel() 时。
【讨论】:
我通过创建我自己的 ControllerActionInvoker 的 impl 实现了“SkipValidationAttribute”方法,它从方法参数中获取属性,如果它被声明,它会向注入 ModelBinder 的模型状态添加一个键(BindModel方法),并在模型绑定时再次将其删除。然后我可以在 ModelBinder 中检查模型状态是否包含密钥,如果它包含它,它会跳过验证,如果不只是像通常那样执行验证。 @Kugel:尽管为时已晚,但 NonValidatatingModelBinder 你必须自己实现。【参考方案4】:我绝对不喜欢 2.0 版本中的这一添加,因为正如您在问题中所说,验证在服务层中更有意义。通过这种方式,您可以在其他非 Web 应用程序中重用它,并更轻松地对其进行测试,而无需模拟自动验证机制。
Controller 层的验证是没有意义的,因为在这部分你只能验证模型数据,不能验证业务规则。例如,考虑一个负责添加新 cmets 的服务和一个想要发布新 cmets 的用户,他/她发布的评论中的数据可能是有效的,但是如果用户因为行为不当而被禁止发表评论会发生什么在过去?您应该在服务层中进行一些验证以确保不会发生这种情况,如果发生,则抛出异常。总之,验证必须在Service层进行。
我使用 xVal 作为我的验证框架,因为它与 DataAnnotationModel 兼容,允许我将验证放置在我想要的任何位置,并且无需额外的努力即可执行客户端验证,甚至是远程客户端。这是我在每个服务开始时使用它的方式,例如登录服务:
public void SignIn(Login login)
var loginErrors = DataAnnotationsValidationRunner.GetErrors(login);
// Model validation: Empty fields?
if (loginErrors.Any())
throw new RulesException(loginErrors);
// Business validation: Does the user exist? Is the password correct?
var user = this._userRepository.GetUserByEmail(login.Email);
if (user == null || user.Password != login.Password)
throw new RulesException(null, "Username or password invalids");
// Other login stuff...
简单、独立于网络且容易...然后,在控制器中:
public RedirectResult Login(Login login)
// Login the user
try
this._authenticationRepository.SignIn(login);
catch (RulesException e)
e.AddModelStateErrors(base.ModelState, "Login");
// Redirect
if (base.ModelState.IsValid)
return base.Redirect(base.Url.Action("Home"));
else return base.Redirect(base.Url.Action("Login"));
【讨论】:
【参考方案5】:在检查模型是否有效之前,只需删除您不需要的项目
ModelState.Remove("Email");
if (ModelState.IsValid)
// your logic
【讨论】:
这个更快更简单! 这很短,很干净而且很有帮助。【参考方案6】:我已经用这段代码解决了这个问题:
public ActionResult Totals(MyModel model)
ModelState.Clear();
return View(model);
但不确定这是正确的方式。
【讨论】:
模型状态数据不仅仅是保存验证。通过清除模型状态,您也会失去其他功能。【参考方案7】:我知道这已经得到解答,但您真正需要的是扩展 DataAnnotationsValidatorProvider
并覆盖 GetValidators
方法。
然后,在启动时,您将从 ModelValidatorProviders.Providers
中删除 DataAnnotationsValidatorProvider
并添加新的。
不用说,如果您根本不想要任何验证,您可以简单地让 GetValidators
方法返回一个空集合。
在我的情况下,我只需要在提交表单时删除验证,同时仍然保持客户端验证,因此如下:
public class DynamicModelValidatorProvider : DataAnnotationsModelValidatorProvider
GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
if (context.HttpContext.Request.HttpMethod == "POST")
return new ModelValidator[] ;
return base.GetValidators(metadata, context, attributes);
【讨论】:
以上是关于在 Asp.Net MVC 中禁用模型验证的主要内容,如果未能解决你的问题,请参考以下文章