4

「下書きを保存」と「公開」という複数の送信ボタンを備えた mvc フォームがあります。目的は、[ドラフトを保存] ボタンをクリックしてフォームを送信したときに、クライアント側 (javascript/unobstructive) の検証とサーバー側の検証の両方をスキップすることです。ただし、「公開」ボタンがクリックされた場合は、両方の検証をトリガーする必要があります。

私の研究は、私にいくつかの解決策をもたらしました。

クライアント側 - jquery プラグインを作成する

    (function ($) {
        $.fn.turnOffValidation = function (form) {
            var settings = form.validate().settings;

            for (var ruleIndex in settings.rules) {
                delete settings.rules[ruleIndex];
            }
        };
    })(jQuery); 

そしてそれを次のように呼び出します

    $('#btnSaveDraft').click(function () {
        $(this).turnOffValidation(jQuery('#myForm'));
    });

サーバー側-しかし、サーバー側の場合、私が見つけることができる唯一の解決策は、ModelState からエラーを削除することです。再利用可能で使いやすいように、アクション属性でそれを行いました。

[AttributeUsage(AttributeTargets.All)]
public class IgnoreValidationAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var modelState = filterContext.Controller.ViewData.ModelState;

        //modelState.Clear();
        foreach (var modelValue in modelState.Values)
        {
            modelValue.Errors.Clear();
        }
    }
}

しかし、これは私の目的を完全には果たしません。エラーの発生を防ぐことができるのに、なぜ検証をトリガーしてエラーをクリアする必要があるのでしょうか。これは可能ですか?

検証結果のエラーをクリアするのではなく、最初にサーバーの検証が行われないようにする方法はありますか?

4

3 に答える 3

1

利用可能なオプションの 1 つは、ModelBinder をオーバーライドすることです。こちらを参照してください

具体的には、OnPropertyValidating をオーバーライドして false を返すと、検証関数が希望どおりに実行されなくなります。

MVC は、メタデータ (検証属性) を読み取り、それらを反復処理するという点で、まだ少し作業を行っています。

いずれにせよ、ModelBinder は検証ロジックを呼び出すものであるため、検討する必要がある拡張ポイントです。

このリンクを参照してくださいASP.MVC 拡張ポイント

于 2013-06-24T13:09:23.470 に答える
1

オーバーフローとビラル、私の質問に答えてくれてありがとう。

@Bilal:保存と送信に同じモデルを使用していますが、モデルに属性は必要なく、コントローラー/アクション レベルで何かが必要です。

より良い答えを見つけるための検索で、私はこのようなものを思いつきました。別の記事からこれを読みましたが、リンクを失いました。入手次第、同じように更新します。

新しいアクション フィルター属性を追加する

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class IgnoreValidationAttribute : FilterAttribute, IAuthorizationFilter
{
    // TODO: Try to put it on another more appropriate method such as OnActionExcecuting.
    // Looks like - This is the earliest method we can interpret before an action. I really dont like this!
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        //TODO: filterContext != null && filterContext.httpContext != null
        var itemKey = this.CreateKey(filterContext.ActionDescriptor);
        if (!filterContext.HttpContext.Items.Contains(itemKey))
        {
            filterContext.HttpContext.Items.Add(itemKey, true);
        }
    }

    private string CreateKey(ActionDescriptor actionDescriptor)
    {
        var action = actionDescriptor.ActionName.ToLower();
        var controller = actionDescriptor.ControllerDescriptor.ControllerName.ToLower();
        return string.Format("IgnoreValidation_{0}_{1}", controller, action);
    }
}

DataAnnotationModelMetadata をオーバーライドする

public class IgnoreValidationModelMetaData : DataAnnotationsModelMetadata
{
    public IgnoreValidationModelMetaData(DataAnnotationsModelMetadataProvider provider, Type containerType,
            Func<object> modelAccessor, Type modelType, string propertyName,
            DisplayColumnAttribute displayColumnAttribute) :
        base(provider, containerType, modelAccessor, modelType, propertyName, displayColumnAttribute)
    {
    }

    public override IEnumerable<ModelValidator> GetValidators(ControllerContext context)
    {
        var itemKey = this.CreateKey(context.RouteData);

        if (context.HttpContext.Items[itemKey] != null && bool.Parse(context.HttpContext.Items[itemKey].ToString()) == true)
        {
            return Enumerable.Empty<ModelValidator>();
        }

        return base.GetValidators(context);
    }

    private string CreateKey(RouteData routeData)
    {
        var action = (routeData.Values["action"] ?? null).ToString().ToLower();
        var controller = (routeData.Values["controller"] ?? null).ToString().ToLower();
        return string.Format("IgnoreValidation_{0}_{1}", controller, action);
    }
}

IgnoreValidationAttribute がアクション メソッドに存在する場合は、カスタム データ注釈メタデータを使用し、検証を空にするようにプロバイダーに指示します。

public class IgnoreValidationModelMetaDataProvider : DataAnnotationsModelMetadataProvider
{
    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes,
      Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
    {
        var displayColumnAttribute = new List<Attribute>(attributes).OfType<DisplayColumnAttribute>().FirstOrDefault();

        var baseMetaData = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);

        // is there any other good strategy to copy the properties?
        return new IgnoreValidationModelMetaData(this, containerType, modelAccessor, modelType, propertyName, displayColumnAttribute)
        {
            TemplateHint = baseMetaData.TemplateHint,
            HideSurroundingHtml = baseMetaData.HideSurroundingHtml,
            DataTypeName = baseMetaData.DataTypeName,
            IsReadOnly = baseMetaData.IsReadOnly,
            NullDisplayText = baseMetaData.NullDisplayText,
            DisplayFormatString = baseMetaData.DisplayFormatString,
            ConvertEmptyStringToNull = baseMetaData.ConvertEmptyStringToNull,
            EditFormatString = baseMetaData.EditFormatString,
            ShowForDisplay = baseMetaData.ShowForDisplay,
            ShowForEdit = baseMetaData.ShowForEdit,
            Description = baseMetaData.Description,
            ShortDisplayName = baseMetaData.ShortDisplayName,
            Watermark = baseMetaData.Watermark,
            Order = baseMetaData.Order,
            DisplayName = baseMetaData.DisplayName,
            IsRequired = baseMetaData.IsRequired
        };
    }
}

使用法

[HttpPost]
    [IgnoreValidation]
    public ActionResult SaveDraft(MyModel myModel)
    {
        if (ModelState.IsValid)
        {
            // Should always reach here
        }

        .......
    }

    [HttpPost]
    public ActionResult Submit(MyModel myModel)
    {
        if (ModelState.IsValid)
        {
        }
    }

ワイヤアップのために Application_Start でこれを呼び出すことを忘れないでください。

ただし、いくつかの懸念があります。

  1. OnAuthorization() よりも HttpContext を操作できる初期の場所はありますか?. 承認に関係のないことをするためにこれをオーバーライドするという考えは好きではありません。OnActionExecuting() は、MVC パイプラインでこれを行うには遅すぎることに注意してください (私はこれを試しましたが、機能していません)。

  2. キーを HttpContext に追加して後で使用するよりも、これを行うためのより良い方法はありますか?

于 2013-06-24T16:08:22.943 に答える
1

と呼ばれるviewModelに変数を導入できますIsDraft

次に、viewModelを次から派生させますIValidatableObject

次に、そのメソッドを次のように実装します: (カスタムサーバー側の検証の例にすぎません)

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (!IsDraft && StartDate > EndDate)
        {
            yield return new ValidationResult("Start date should be less than end date", new[] { "StartDate" });
        }
    }

このようにして、ドラフトではない場合にのみサーバー側の検証がトリガーされます。

クライアント側の検証では、実装を使用しますIClientValidatable

これは方法です:

public IEnumerable<modelclientvalidationrule> GetClientValidationRules 
(ModelMetadata metadata, ControllerContext context)
{

}

これは、検証を無効にするよりも優れたアプローチだと思います。

カスタムのクライアント側検証の実装についてサポートが必要な場合は、次のリンクを参照してください。

それが役立つことを願っています

于 2013-06-22T06:09:46.393 に答える