4 votes

Activer/désactiver dynamiquement la validation côté serveur de mvc

J'ai un formulaire mvc avec multiples boutons de soumission - "Enregistrer le projet" et "Publier". L'objectif est d'ignorer la validation côté client (javascript/unobstructive) et la validation côté serveur lorsque le bouton "Enregistrer le brouillon" est cliqué et que le formulaire est soumis. Mais je dois déclencher les deux validations si le bouton "Publier" est cliqué.

Mes recherches m'ont conduit à quelques solutions.

Côté client -En écrivant un plugin jquery

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

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

et l'invoquer comme

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

Côté serveur - Mais pour le côté serveur, la seule solution que j'ai trouvée est de supprimer les erreurs du ModelState. Je l'ai fait dans un Attribut d'Action pour qu'il soit réutilisable et facile à utiliser.

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

Mais cela ne répond pas complètement à mon objectif. Pourquoi devrions-nous déclencher la validation et effacer les erreurs si nous pouvons éviter que cela ne se produise ? Est-ce possible ?

Existe-t-il des moyens d'empêcher la validation du serveur au lieu de supprimer les erreurs résultant de la validation ?

1voto

Bilal Fazlani Points 1318

Vous pouvez introduire une variable dans votre viewModel appelée IsDraft .

Dérivez ensuite votre viewModel de IValidatableObject

Puis implémentez sa méthode comme ceci : (juste un exemple de validation personnalisée côté serveur)

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

De cette manière, la validation côté serveur ne sera déclenchée que si elle n'est pas ébauchée.

Pour la validation côté client, il convient d'utiliser l'implémentation IClientValidatable

Il s'agit de la méthode :

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

}

Je pense qu'il s'agit d'une meilleure approche que de désactiver la validation.

Consultez ces liens si vous avez besoin d'aide pour mettre en œuvre des validations personnalisées côté client :

J'espère que cela vous aidera

1voto

stevenrcfox Points 828

L'une des options disponibles consiste à remplacer ModelBinder, voir ici .

Plus précisément, le fait de surcharger OnPropertyValidating et de renvoyer false empêche la fonction de validation de s'exécuter comme vous le souhaitez.

MVC effectue encore un peu de travail en lisant les métadonnées (attributs de validation) et en les parcourant.

Quoi qu'il en soit, ModelBinder est le point d'extensibilité que vous devez examiner, car c'est lui qui appelle la logique de validation.

Voir ce lien Points d'extensibilité ASP.MVC

1voto

int-i Points 31

Overflow et Bilal, Merci d'avoir répondu à ma question.

@Bilal : J'utilise le même modèle pour Enregistrer et Soumettre et je ne veux pas d'attributs sur le modèle, j'ai plutôt besoin de quelque chose au niveau du contrôleur/de l'action.

En cherchant à trouver une meilleure réponse, j'ai abouti à quelque chose comme ceci. Je l'ai lu dans un autre article, mais j'ai perdu le lien. Dès que je l'aurai retrouvé, je le mettrai à jour.

Ajouter un nouvel attribut de filtre d'action

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

Surcharge du modèle 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);
    }
}

Indiquons maintenant au fournisseur d'utiliser nos métadonnées d'annotation de données personnalisées et de vider la validation si IgnoreValidationAttribute est présent dans une méthode d'action.

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

Utilisation

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

        .......
    }

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

N'oubliez pas d'appeler ceci dans votre Application_Start pour le câblage 'ModelMetadataProviders.Current = new IgnoreValidationModelMetaDataProvider() ;

Il y a cependant quelques inquiétudes à avoir.

  1. Existe-t-il un endroit où nous pourrions manipuler le HttpContext plus tôt que OnAuthorization() ? Je n'aime pas l'idée de surcharger cette fonction pour faire quelque chose qui n'est pas lié à l'autorisation. Veuillez noter que OnActionExecuting() sera trop tard dans le pipeline MVC pour faire cela (j'ai essayé et cela ne fonctionne pas).

  2. Existe-t-il une meilleure façon de procéder que d'ajouter une clé à HttpContext et de l'utiliser ultérieurement ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X