4 votes

MVC3 Custom Unobtrusive Validator on a List of Objects

La question de base pour commencer : Comment pouvez-vous mettre un validateur personnalisé et discret sur une liste d'objets dans votre modèle ? Par exemple, si mon modèle autorise plusieurs téléchargements de fichiers, j'ai donc une liste de fichiers et je veux que mon validateur s'exécute sur chacun de ces fichiers ?

Maintenant, un exemple spécifique. J'ai un validateur personnalisé et discret qui vérifie si une extension de fichier ne se trouve pas dans une liste d'extensions interdites :

public class FileExtensionValidatorAttribute : ValidationAttribute, IClientValidatable {

    protected static string[] PROHIBITED_EXTENSIONS = {
        // ... Liste des extensions que je n'autorise pas.
    };

    public override bool IsValid(object value) {
        if (value is IEnumerable) {
            foreach (var file in (IEnumerable)value) {
                var fileName = file.FileName;
                if (PROHIBITED_EXTENSIONS.Any(x => fileName.EndsWith(x))) return false;
            }
        } else {
            var file = (HttpPostedFileBase)value;
            var fileName = file.FileName;
            if (PROHIBITED_EXTENSIONS.Any(x => fileName.EndsWith(x))) return false;
        }

        return true;
    }

    public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context) {
        var modelClientVlidationRule = new ModelClientValidationRule {
            ErrorMessage = this.ErrorMessageString,
            ValidationType = "fileextension",
        };
        modelClientVlidationRule.ValidationParameters.Add("prohibitedextensions", string.Join("|", PROHIBITED_EXTENSIONS));

        yield return modelClientVlidationRule;
    }
}

Notez dans mon IsValid que j'ai construit cela pour accepter un seul fichier ou une liste de fichiers.

Dans ma classe de modèle, je peux utiliser ceci sur un seul HttpPostedFileBase :

[FileExtensionValidator(ErrorMessage = "Extension non valide")]
public HttpPostedFileBase Upload { get; set; }

Ensuite, je m'attache au validateur de jQuery dans ma vue :

jQuery.validator.addMethod("fileExtension", function (value, element, param) {
    var extension = "";
    var dotIndex = value.lastIndexOf('.');
    if (dotIndex != -1) extension = value.substring(dotIndex + 1).toLowerCase();

    return $.inArray(extension, param.prohibitedExtensions) === -1;
});

jQuery.validator.unobtrusive.adapters.add('fileextension', ['prohibitedextensions'], function (options) {
    options.rules['fileExtension'] = {
        prohibitedExtensions: options.params.prohibitedextensions.split('|')
    };
    options.messages['fileExtension'] = options.message;
});

Tout cela fonctionne très bien, côté client et côté serveur ... mais seulement sur un seul HttpPostedFileBase. Le problème est que je dois donner aux utilisateurs la possibilité de télécharger un ou plusieurs fichiers. Si je change mon modèle comme ceci :

[FileExtensionValidator(ErrorMessage = "Extension non valide")]
public List Uploads { get; set; }

... la validation côté client ne fonctionne plus ; seul le côté serveur fonctionne. Cela est évident en regardant le code source. La balise générée manque de tous les attributs data-val dont elle a besoin pour s'exécuter. En effectuant un débogage, GetClientValidationRules n'est jamais appelé.

Que me manque-t-il ?

Est-ce à cause de la façon dont je le rends ? J'utilise simplement un EditorTemplate pour HttpPostedFileBase :

@model System.Web.HttpPostedFileBase
@Html.TextBoxFor(m => m, new { type = "file", size = 60 })

... et ma vue le rend comme ceci :

@Html.EditorFor(m => m.Uploads)

Tout conseil est apprécié.

3voto

Ber'Zophus Points 1000

Voici ce que j'ai trouvé.

En fait, je pense que le problème est finalement causé parce que le MVC ne sait pas que je veux que cette Annotation de Données sur la Liste soit appliquée à tous ses membres. Ni ne devrait-il le faire, je suppose.

J'ai donc simplement créé un "viewmodel" autour de HttpPostedFileBase, et j'ai mis mon validateur là :

public class UploadedFile {
    [FileExtensionValidator(ErrorMessage = "Extension invalide")]
    public HttpPostedFileBase File { get; set; }
}

Ensuite, dans mon modèle réel, j'utilise maintenant simplement une liste de ceux-ci :

public List Uploads { get; set; }

...sans plus d'annotations de données ici bien sûr puisqu'elles sont maintenant dans UploadedFile.

Ensuite, avec des modifications mineures à la vue et au modèle d'éditeur pour utiliser ceux-ci, cela fonctionne maintenant parfaitement, côté client et côté serveur. (Cependant, cela me semble maladroit. Si quelqu'un a une manière plus simple, je suis toujours prêt à l'entendre.)

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