web-dev-qa-db-ja.com

特定の状況下で必須検証属性を無効にする

特定のコントローラーアクションでRequired validation属性を無効にできるかどうか疑問に思っていました。これは、編集フォームの1つで、ユーザーが既に以前に指定したフィールドの値を入力する必要がないためです。ただし、値を入力するときに、値のハッシュなどの特別なロジックを使用してモデルを更新するロジックを実装します。

この問題を回避する方法についての提案はありますか?

編集:
そして、はい、クライアント検証はここで問題になります。値を入力せずにフォームを送信することはできません。

122

この問題は、ビューモデルを使用して簡単に解決できます。ビューモデルは、特定のビューのニーズに合わせて特別に調整されたクラスです。たとえば、あなたの場合、次のビューモデルを使用できます。

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

クライアント側の単一フィールドの検証を無効にする場合は、次のように検証属性をオーバーライドできます。

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

私はこの質問がずっと前に答え​​られており、受け入れられた答えが実際に仕事をすることを知っています。ただし、気になる点が1つあります。検証を無効にするために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)
{
    ...
}
39
PhilDulac

コントローラーアクションで次を使用して、プロパティからすべての検証を削除できます。

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

@ Ian's MVC5に関するコメント

以下はまだ可能です

ModelState.Remove("PropertyNameInModel");

更新されたAPIを使用すると、静的な型付けが失われるのは少し面倒です。 HTMLヘルパーのインスタンスを作成し、 NameExtensionsメソッド を使用することで、古い方法に似たものを実現できます。

19
jps

クライアント側フォームの検証を無効にするために、私の研究に基づいた複数のオプションを以下に示します。そのうちの1つがうまくいけばうまくいきます。

オプション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サーバー側の検証を動的に有効/無効にする

14
int-i

個人的には、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;
}
14
nttakr

ここで最もクリーンな方法は、クライアント側の検証を無効にすることであり、サーバー側では次のことを行う必要があります。

  1. ModelState ["SomeField"]。Errors.Clear(コントローラー内で、またはコントローラーコードが実行される前にエラーを削除するアクションフィルターを作成します)
  2. 検出された問題の違反を検出した場合、コントローラーコードからModelState.AddModelErrorを追加します。

これらの「事前回答」フィールドの数は異なる可能性があるため、ここでカスタムビューモデルを使用しても問題は解決しないようです。そうでない場合、カスタムビューモデルが実際に最も簡単な方法かもしれませんが、上記の手法を使用すると、検証の問題を回避できます。

7

これはコメントで他の誰かの答えでした...しかしそれは本当の答えでなければなりません:

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

[Required]属性を持つフィールドを持つMVC 6でテスト済み

https://stackoverflow.com/users/73382/rob 上記から盗まれた回答

5

モデルの編集ビューを作成するときにこの問題が発生し、1つのフィールドのみを更新したい。

最も簡単な方法の私の解決策は、次の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エンティティ

2
Felipe FMMobile

知っている限り、実行時に属性を削除することはできませんが、値を変更するだけです(つまり、読み取り専用のtrue/false) 同様のものを探してください 他のコントローラーが必要とするロジックを壊さずにすべてのロジックを挿入できるように、属性をいじらずに必要なことを行う別の方法として、特定のアクションのViewModelを使用します。ある種のウィザード(マルチステップフォーム)を取得しようとする場合は、代わりに、既にコンパイルされたフィールドをシリアル化し、TempDataを使用してそれらをステップに沿って移動できます。 (シリアライズとデシリアライズのヘルプについては、 MVC futures を使用できます)

1
Iridio

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
    {

    }
}

ソース:

1
Roberto B

@Darinが言ったことは、私もお勧めします。ただし、実際にはこのメソッドをbit、boolなどのプリミティブ型、さらにはGuidなどの構造体に対しても単純にNULL可能にすることで使用できることを(およびコメントの1つに応じて)追加します。これを実行すると、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
}
1
Nick Albrecht

MVC 5以降、これはglobal.asaxに追加することで簡単に実現できます。

DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
1
Yom S.

別の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; }
}
0
Nithin Chandran