在某些情况下禁用必需的验证属性

时间:2011-03-20 07:34:45

标签: c# asp.net-mvc asp.net-mvc-3 data-annotations

我想知道是否可以在某些控制器操作中禁用Required validation属性。我想知道这是因为在我的一个编辑表单中,我不需要用户输入他们之前已经指定的字段的值。然而,我然后实现逻辑,当它们输入值时,它使用一些特殊的逻辑来更新模型,例如散列值等。

有关如何解决此问题的任何消息?

编辑:
是的,客户端验证是一个问题,因为它不允许他们在不输入值的情况下提交表单。

16 个答案:

答案 0 :(得分:72)

使用视图模型可以轻松解决此问题。视图模型是专门针对给定视图的需求而定制的类。例如,在您的情况下,您可以使用以下视图模型:

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)
{
    ...
}

答案 1 :(得分:54)

如果您只想在客户端禁用单个字段的验证,则可以按如下方式覆盖验证属性:

@Html.TexBoxFor(model => model.SomeValue, 
                new Dictionary<string, object> { { "data-val", false }})

答案 2 :(得分:39)

我知道很久以前这个问题已得到解答,接受的答案实际上会完成。但是有一件事困扰着我:只需要复制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)
{
    ...
}

答案 3 :(得分:18)

您可以在控制器操作中使用以下内容删除属性的所有验证。

ModelState.Remove<ViewModel>(x => x.SomeProperty);

@Ian's关于MVC5的评论

以下仍然可能

ModelState.Remove("PropertyNameInModel");

有点烦人的是你丢失了使用更新的API的静态类型。您可以通过创建HTML帮助程序实例并使用NameExtensions Methods来实现与旧方法类似的操作。

答案 4 :(得分:14)

我个人倾向于使用Darin Dimitrov在他的解决方案中展示的方法。 这使您能够使用数据注释方法进行验证,并在与当前任务相对应的每个ViewModel上具有单独的数据属性。 要最小化模型和视图模型之间的复制工作量,您应该查看AutoMapper或ValueInjecter。两者都有各自的优势,所以请检查两者。

另一种可能的方法是从IValidatableObject派生您的viewmodel或模型。这使您可以选择实现Validate函数。 在validate中,您可以返回ValidationResult元素列表,也可以为您在验证中检测到的每个问题发出一个yield return。

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;
}

答案 5 :(得分:14)

客户端 为了禁用表单验证,下面给出了基于我的研究的多个选项。希望其中一个能为你效劳。

选项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();
        }
    }
}

这里描述了一种更好的方法Enable/Disable mvc server side validation dynamically

答案 6 :(得分:7)

我认为这里最干净的方法是禁用客户端验证,在服务器端你需要:

  1. ModelState [“SomeField”]。Errors.Clear(在您的控制器中或创建一个动作过滤器以在执行控制器代码之前删除错误)
  2. 当您检测到违反检测到的问题时,从控制器代码中添加ModelState.AddModelError。
  3. 似乎即使是自定义视图模型也不会解决问题,因为那些“预先回答”字段的数量可能会有所不同。如果他们不这样做自定义视图模型可能确实是最简单的方法,但使用上述技术可以解决您的验证问题。

答案 7 :(得分:5)

这是评论中其他人的答案......但它应该是一个真正的答案:

$("#SomeValue").removeAttr("data-val-required")

使用具有[Required]属性

的字段在MVC 6上进行测试

从上面的https://stackoverflow.com/users/73382/rob偷来的

答案 8 :(得分:2)

当我为我的模型创建编辑视图时,我遇到了这个问题,我想只更新一个字段。

我对最简单方法的解决方案是使用以下两个字段:

 <%: Html.HiddenFor(model => model.ID) %>
 <%: Html.HiddenFor(model => model.Name)%>
 <%: Html.HiddenFor(model => model.Content)%>
 <%: Html.TextAreaFor(model => model.Comments)%>

评论是我仅在编辑视图中更新的字段,其中没有必需属性。

ASP.NET MVC 3实体

答案 9 :(得分:1)

AFAIK您无法在运行时删除属性,只能更改其值(即:readonly true / false)look here for something similar。 作为另一种在不干扰属性的情况下执行所需操作的方法,我将使用ViewModel进行特定操作,这样您就可以插入所有逻辑而不会破坏其他控制器所需的逻辑。 如果您尝试获取某种向导(多步骤表单),则可以序列化已编译的字段,并使用TempData将它们带到您的步骤中。 (有关序列化反序列化的帮助,您可以使用MVC futures

答案 10 :(得分:1)

@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
}

答案 11 :(得分:1)

从MVC 5开始,可以通过在global.asax中添加它来轻松实现。

DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;

答案 12 :(得分:1)

我一直在寻找一种解决方案,可以在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
    {

    }
}

来源:

答案 13 :(得分:0)

如果您不想使用其他ViewModel,则可以在视图上禁用客户端验证,还可以删除服务器上要忽略的属性的验证。请查看此答案以获得更深入的解释https://stackoverflow.com/a/15248790/1128216

答案 14 :(得分:0)

在我的情况下,出于重复使用的目的,在许多页面中使用了相同的模型。所以我所做的就是创建了一个自定义属性,用于检查排除项

def lda_fit(X,Y):

    # class means


    unique_classes=np.unique(Y)
    mu=np.zeros((len(unique_classes),X.shape[1]))
    for i,name in enumerate(unique_classes):
        mu[i,:] = X[Y==name,:].mean(axis=0)


    mupos=mu[1]
    muneg=mu[0]
    mupos=mupos.reshape(155,2)
    muneg=muneg.reshape(155,2)
    Xneu=X[0].reshape(155,2)

    # D-by-D inter class covariance matrix (signal)
    Sinter = np.dot((muneg-mupos),(muneg-mupos).T)


    # D-by-D intra class covariance matrices (noise)
    Sintra =np.dot((Xneu-mupos),(Xneu-mupos).T)+np.dot((Xneu-muneg),(Xneu-muneg).T)

    # solve eigenproblem
    eigvals, eigvecs = sp.linalg.eig(Sinter,Sintra)
    w = eigvecs[:,eigvals.argmax()]
    # bias term
    b = (w.dot(mupos) + w.dot(muneg))/2.
    # return the weight vector
    return w,b

lda_fit(X,Y)

和您的控制器中

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           

    }

答案 15 :(得分:-1)

是的,可以禁用必需属性。从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;

这感觉很干净吗?

注意:当您的对象实例处于活动状态时,将禁用上述验证\ active ...