我想知道是否可以在某些控制器操作中禁用“必需的验证”属性。我想知道这是因为在我的一种编辑表单上,我不需要用户为他们先前已经指定的字段输入值。但是,我然后实现了一种逻辑,即当他们输入值时,它会使用一些特殊的逻辑来更新模型,例如对值进行哈希处理等。
关于如何解决此问题有任何建议吗?
编辑:
是的,客户端验证在这里是一个问题,因为它将不允许他们在不输入值的情况下提交表单。
我想知道是否可以在某些控制器操作中禁用“必需的验证”属性。我想知道这是因为在我的一种编辑表单上,我不需要用户为他们先前已经指定的字段输入值。但是,我然后实现了一种逻辑,即当他们输入值时,它会使用一些特殊的逻辑来更新模型,例如对值进行哈希处理等。
关于如何解决此问题有任何建议吗?
编辑:
是的,客户端验证在这里是一个问题,因为它将不允许他们在不输入值的情况下提交表单。
Answers:
通过使用视图模型可以轻松解决此问题。视图模型是专门为给定视图的需求量身定制的类。因此,例如,在您的情况下,您可以具有以下视图模型:
public UpdateViewView
{
[Required]
public string Id { get; set; }
... some other properties
}
public class InsertViewModel
{
public string Id { get; set; }
... some other properties
}
将在其相应的控制器操作中使用:
[HttpPost]
public ActionResult Update(UpdateViewView model)
{
...
}
[HttpPost]
public ActionResult Insert(InsertViewModel model)
{
...
}
Update(FormCollection collection)
,至少我从来没有。我总是定义并使用特定的视图模型:Update(UpdateViewView model)
。
Insert
。感谢您指出了这一点。
如果只想在客户端禁用单个字段的验证,则可以按如下所示覆盖验证属性:
@Html.TexBoxFor(model => model.SomeValue,
new Dictionary<string, object> { { "data-val", false }})
@Html.TexBoxFor(model => model.SomeValue, new { data_val = false })
-易于阅读IMO。
$(".search select").attr('data-val', false);
我知道这个问题已经很久以前已经回答了,接受的答案实际上可以完成工作。但是有一件事困扰着我:只复制2个模型就可以禁用验证。
这是我的建议:
public class InsertModel
{
[Display(...)]
public virtual string ID { get; set; }
...Other properties
}
public class UpdateModel : InsertModel
{
[Required]
public override string ID
{
get { return base.ID; }
set { base.ID = value; }
}
}
这样,您就不必为客户端/服务器端验证而烦恼,该框架将按照预期的方式运行。另外,如果您[Display]
在基类上定义了一个属性,则不必在中重新定义它UpdateModel
。
而且您仍然可以以相同的方式使用这些类:
[HttpPost]
public ActionResult Update(UpdateModel model)
{
...
}
[HttpPost]
public ActionResult Insert(InsertModel model)
{
...
}
您可以在控制器操作中使用以下命令删除属性的所有验证。
ModelState.Remove<ViewModel>(x => x.SomeProperty);
@Ian关于MVC5 的评论
仍然有可能
ModelState.Remove("PropertyNameInModel");
令人烦恼的是,使用更新的API会丢失静态类型。通过创建HTML帮助器实例并使用NameExtensions方法,可以实现与旧方法类似的方法。
ModelState
匹配该签名的方法。至少在MVC 5中没有。
客户端 为了禁用表单验证,以下基于我的研究提供了多个选项。其中之一将希望为您工作。
选项1
我喜欢这个,这对我来说非常合适。
(function ($) {
$.fn.turnOffValidation = function (form) {
var settings = form.validate().settings;
for (var ruleIndex in settings.rules) {
delete settings.rules[ruleIndex];
}
};
})(jQuery);
并像这样调用
$('#btn').click(function () {
$(this).turnOffValidation(jQuery('#myForm'));
});
选项2
$('your selector here').data('val', false);
$("form").removeData("validator");
$("form").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse("form");
选项3
var settings = $.data($('#myForm').get(0), 'validator').settings;
settings.ignore = ".input";
选项4
$("form").get(0).submit();
jQuery('#createForm').unbind('submit').submit();
选项5
$('input selector').each(function () {
$(this).rules('remove');
});
服务器端
创建一个属性,并使用该属性标记您的操作方法。自定义此设置以适应您的特定需求。
[AttributeUsage(AttributeTargets.All)]
public class IgnoreValidationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var modelState = filterContext.Controller.ViewData.ModelState;
foreach (var modelValue in modelState.Values)
{
modelValue.Errors.Clear();
}
}
}
此处介绍了一种更好的方法,可以动态启用/禁用mvc服务器端验证
我个人倾向于使用Darin Dimitrov在他的解决方案中展示的方法。这使您腾出了使用数据批注方法进行验证的能力,并使每个ViewModel上具有与手头任务相对应的单独数据属性。为了最大程度地减少在模型和视图模型之间进行复制的工作量,应查看AutoMapper或ValueInjecter。两者都有各自的长处,因此请同时检查它们。
另一种可能的方法是从IValidatableObject派生您的视图模型或模型。这为您提供了实现功能验证的选项。在验证中,您可以返回ValidationResult元素的列表,也可以针对在验证中检测到的每个问题发出收益回报。
ValidationResult由错误消息和带有字段名的字符串列表组成。错误消息将显示在输入字段附近的位置。
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if( NumberField < 0 )
{
yield return new ValidationResult(
"Don't input a negative number",
new[] { "NumberField" } );
}
if( NumberField > 100 )
{
yield return new ValidationResult(
"Don't input a number > 100",
new[] { "NumberField" } );
}
yield break;
}
我认为这里最干净的方法是禁用客户端验证,而在服务器端,您将需要:
似乎这里的自定义视图模型也无法解决问题,因为这些“预回答”字段的数量可能会有所不同。如果他们不这样做,那么自定义视图模型确实可能是最简单的方法,但是使用上述技术,您可以解决验证问题。
这是别人在评论中的答案...但这应该是真实的答案:
$("#SomeValue").removeAttr("data-val-required")
在具有[Required]
属性的字段的MVC 6上进行了测试
当我为模型创建一个“编辑视图”时我遇到了这个问题,我只想更新一个字段。
我最简单的解决方案是使用以下两个字段:
<%: Html.HiddenFor(model => model.ID) %>
<%: Html.HiddenFor(model => model.Name)%>
<%: Html.HiddenFor(model => model.Content)%>
<%: Html.TextAreaFor(model => model.Comments)%>
注释是我仅在“编辑视图”中更新的字段,没有必填属性。
ASP.NET MVC 3实体
@Darin所说的也是我的建议。但是,我要补充一点(作为对其中一项评论的回应),您实际上也可以通过简单地使它们可为空而将该方法用于诸如bit,bool甚至是Guid之类的原始类型。完成此操作后,该Required
属性将按预期运行。
public UpdateViewView
{
[Required]
public Guid? Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public int? Age { get; set; }
[Required]
public bool? IsApproved { get; set; }
//... some other properties
}
我一直在寻找一种解决方案,可以在Web API中使用相同的模型进行插入和更新。在我看来,这始终是一种满足感。的[Requiered]
,如果它是一个更新方法属性必须被跳过。在我的解决方案中,您[IgnoreRequiredValidations]
在方法上方放置了一个属性。如下所示:
public class WebServiceController : ApiController
{
[HttpPost]
public IHttpActionResult Insert(SameModel model)
{
...
}
[HttpPut]
[IgnoreRequiredValidations]
public IHttpActionResult Update(SameModel model)
{
...
}
...
还有什么需要做的?必须在启动时创建并添加自己的BodyModelValidator。这在HttpConfiguration中,如下所示:config.Services.Replace(typeof(IBodyModelValidator), new IgnoreRequiredOrDefaultBodyModelValidator());
using Owin;
using your_namespace.Web.Http.Validation;
[assembly: OwinStartup(typeof(your_namespace.Startup))]
namespace your_namespace
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
Configuration(app, new HttpConfiguration());
}
public void Configuration(IAppBuilder app, HttpConfiguration config)
{
config.Services.Replace(typeof(IBodyModelValidator), new IgnoreRequiredOrDefaultBodyModelValidator());
}
...
我自己的BodyModelValidator是从DefaultBodyModelValidator派生的。而且我发现我必须重写'ShallowValidate'方法。在此替代中,我过滤了所需的模型验证器。现在是IgnoreRequiredOrDefaultBodyModelValidator类和IgnoreRequiredValidations属性类:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web.Http.Controllers;
using System.Web.Http.Metadata;
using System.Web.Http.Validation;
namespace your_namespace.Web.Http.Validation
{
public class IgnoreRequiredOrDefaultBodyModelValidator : DefaultBodyModelValidator
{
private static ConcurrentDictionary<HttpActionBinding, bool> _ignoreRequiredValidationByActionBindingCache;
static IgnoreRequiredOrDefaultBodyModelValidator()
{
_ignoreRequiredValidationByActionBindingCache = new ConcurrentDictionary<HttpActionBinding, bool>();
}
protected override bool ShallowValidate(ModelMetadata metadata, BodyModelValidatorContext validationContext, object container, IEnumerable<ModelValidator> validators)
{
var actionContext = validationContext.ActionContext;
if (RequiredValidationsIsIgnored(actionContext.ActionDescriptor.ActionBinding))
validators = validators.Where(v => !v.IsRequired);
return base.ShallowValidate(metadata, validationContext, container, validators);
}
#region RequiredValidationsIsIgnored
private bool RequiredValidationsIsIgnored(HttpActionBinding actionBinding)
{
bool ignore;
if (!_ignoreRequiredValidationByActionBindingCache.TryGetValue(actionBinding, out ignore))
_ignoreRequiredValidationByActionBindingCache.TryAdd(actionBinding, ignore = RequiredValidationsIsIgnored(actionBinding.ActionDescriptor as ReflectedHttpActionDescriptor));
return ignore;
}
private bool RequiredValidationsIsIgnored(ReflectedHttpActionDescriptor actionDescriptor)
{
if (actionDescriptor == null)
return false;
return actionDescriptor.MethodInfo.GetCustomAttribute<IgnoreRequiredValidationsAttribute>(false) != null;
}
#endregion
}
[AttributeUsage(AttributeTargets.Method, Inherited = true)]
public class IgnoreRequiredValidationsAttribute : Attribute
{
}
}
资料来源:
string debug = new StackTrace().ToString()
找出谁是HANDELING模型验证。如果您不想使用其他ViewModel,则可以在视图上禁用客户端验证,也可以在服务器上删除要忽略的那些属性的验证。请检查此答案以获取更深入的说明https://stackoverflow.com/a/15248790/1128216
在我的情况下,出于重复使用的目的,在许多页面中使用了相同的模型。所以我所做的是我创建了一个自定义属性来检查排除项
public class ValidateAttribute : ActionFilterAttribute
{
public string Exclude { get; set; }
public string Base { get; set; }
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!string.IsNullOrWhiteSpace(this.Exclude))
{
string[] excludes = this.Exclude.Split(',');
foreach (var exclude in excludes)
{
actionContext.ModelState.Remove(Base + "." + exclude);
}
}
if (actionContext.ModelState.IsValid == false)
{
var mediaType = new MediaTypeHeaderValue("application/json");
var error = actionContext.ModelState;
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.OK, error.Keys, mediaType);
}
}
}
并在您的控制器中
[Validate(Base= "person",Exclude ="Age,Name")]
public async Task<IHttpActionResult> Save(User person)
{
//do something
}
说模型是
public class User
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Range(18,99)]
public string Age { get; set; }
[MaxLength(250)]
public string Address { get; set; }
}
是的,可以禁用必需属性。从RequiredAtribute创建您自己的自定义类属性(示例代码称为ChangeableRequired),并添加Disabled属性,并覆盖IsValid方法以检查其是否被取消平衡。使用反射来设置禁用的属性,如下所示:
自定义属性:
namespace System.ComponentModel.DataAnnotations
{
public class ChangeableRequired : RequiredAttribute
{
public bool Disabled { get; set; }
public override bool IsValid(object value)
{
if (Disabled)
{
return true;
}
return base.IsValid(value);
}
}
}
更新您的属性以使用新的自定义属性:
class Forex
{
....
[ChangeableRequired]
public decimal? ExchangeRate {get;set;}
....
}
在需要禁用该属性的地方,请使用反射进行设置:
Forex forex = new Forex();
// Get Property Descriptor from instance with the Property name
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(forex.GetType())["ExchangeRate"];
//Search for Attribute
ChangeableRequired attrib = (ChangeableRequired)descriptor.Attributes[typeof(ChangeableRequired)];
// Set Attribute to true to Disable
attrib.Disabled = true;
感觉好干净吗?
注意:当您的对象实例处于活动状态时,以上验证将被禁用。
RequiredAttr
完全删除并在需要时进行服务器端检查。但这对客户来说是棘手的