特定のコントローラーアクションで必須検証属性を無効にできるかどうか疑問に思っていました。編集フォームの1つで、ユーザーが以前に指定したフィールドに値を入力する必要がないので、これは不思議に思っています。ただし、その後、値を入力するときに、値のハッシュなどの特別なロジックを使用してモデルを更新するロジックを実装します。
この問題を回避する方法に関する提案はありますか?
編集:
そして、はい、クライアントの検証はここで問題になります。値を入力しないとフォームを送信することができないからです。
特定のコントローラーアクションで必須検証属性を無効にできるかどうか疑問に思っていました。編集フォームの1つで、ユーザーが以前に指定したフィールドに値を入力する必要がないので、これは不思議に思っています。ただし、その後、値を入力するときに、値のハッシュなどの特別なロジックを使用してモデルを更新するロジックを実装します。
この問題を回避する方法に関する提案はありますか?
編集:
そして、はい、クライアントの検証はここで問題になります。値を入力しないとフォームを送信することができないからです。
回答:
この問題は、ビューモデルを使用して簡単に解決できます。ビューモデルは、特定のビューのニーズに合わせて特別に調整されたクラスです。したがって、たとえば、あなたの場合、次のビューモデルを使用できます。
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);
私はこの質問がずっと前に回答されており、受け入れられた回答が実際に機能することを知っています。しかし、気になることが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)
{
...
}
コントローラーアクションで次のようにすると、プロパティからすべての検証を削除できます。
ModelState.Remove<ViewModel>(x => x.SomeProperty);
MVC5に関する@Ian のコメント
以下はまだ可能です
ModelState.Remove("PropertyNameInModel");
更新されたAPIを使用すると、静的な型付けが失われることに少し不愉快です。HTMLヘルパーのインスタンスを作成し、NameExtensionsメソッドを使用することにより、古い方法と同様のことを実現できます。
ModelState
そのシグネチャに一致するメソッドはありません。少なくともMVC 5にはありません。
クライアント側 フォームの検証を無効にするために、私の調査に基づく複数のオプションを以下に示します。それらの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サーバー側検証を有効/無効にします
個人的には、Darin Dimitrovが彼のソリューションで示したアプローチを使用する傾向があります。これにより、検証でデータ注釈アプローチを使用できるようになり、手元のタスクに対応する各ViewModelに個別のデータ属性を持つことができます。モデルとビューモデル間でコピーする作業量を最小限に抑えるには、AutoMapperまたはValueInjecterを調べる必要があります。どちらもそれぞれに長所があるのでチェックしてみてください。
IValidatableObjectからビューモデルまたはモデルを派生させることもできます。これにより、関数Validateを実装するオプションが提供されます。検証では、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でテスト済み
上記のhttps://stackoverflow.com/users/73382/robから盗まれた答え
モデルの編集ビューを作成しているときにこの問題が発生し、フィールドを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エンティティ
私の知る限り、実行時に属性を削除することはできませんが、それらの値を変更するだけです(つまり、readonly true / false)。ここで同様の何かを探してください。属性をいじらずに必要なことを行う別の方法として、特定のアクションのViewModelを使用して、他のコントローラーが必要とするロジックを壊すことなくすべてのロジックを挿入できるようにします。ある種のウィザード(マルチステップフォーム)を取得しようとする場合は、代わりに既にコンパイル済みのフィールドをシリアル化し、TempDataを使用してそれらをステップに沿って持ってくることができます。(シリアライズのデシリアライズを支援するために、MVCフューチャーを使用できます)
@Darinが言ったことは、私もお勧めするものです。ただし、それに加えて(コメントの1つに応じて)、ビット、ブール、Guidなどのプリミティブ型にもこのメソッドを使用して、単にnull可能にすることもできます。これを行うと、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]
それはupdateメソッドである場合、属性はスキップされなければなりません。私のソリューションでは[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()
して、モデルの検証を誰が行っているかを調べます。別の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メソッドをオーバーライドして、無効化されているかどうかを確認します。次のように、リフレクションを使用して無効化されたpopertyを設定します。
カスタム属性:
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;
これはすてきで清潔な感じですか?
注:上記の検証は、オブジェクトインスタンスがalive \ activeである間は無効になります...
RequiredAttr
完全に削除し、必要に応じてサーバー側のチェックを行うことです。しかし、これはクライアントにとってトリッキーです