Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC 3: DataAnnotations.FileExtensionsAttribute not working

According to the MSDN documentation, by default the FileExtensionsAttribute (.NET 4.5) should allow me to only upload only jpg,jpeg,gif and png files - which is what I want.

I tried uploading a jpg without the attribute, it works. Great. Then I added the attribute to my view model..

[FileExtensions(ErrorMessage = "Please specify a valid image file (.jpg, .jpeg, .gif or .png)")]
public HttpPostedFileBase ImageFile { get; set; }

No joy. The verification fails and the ErrorMessage is shown. On top of that there doesn't seem to be a way to specify any allowed custom file extensions. I ended up extending the FileExtensionsAttribute and using my own verification logic, which works as expected. But why doesn't this way work?

Will post the entire controller and view if required. I used this example as a basis for the uploading logic, but using the DataAnnotations.FileExtensionsAttribute instead of Microsoft.Web.Mvc.FileExtensions.. How do I upload images in ASP.NET MVC?

like image 367
Greig Stewart Avatar asked Dec 16 '11 15:12

Greig Stewart


3 Answers

Since System.ComponentModel.DataAnnotations.FileExtensionsAttribute is sealed. I use a wrapper for MVC 4.

public class HttpPostedFileExtensionsAttribute : DataTypeAttribute, IClientValidatable
{
    private readonly FileExtensionsAttribute _innerAttribute =
        new FileExtensionsAttribute();

    /// <summary>
    ///     Initializes a new instance of the <see cref="HttpPostedFileExtensionsAttribute" /> class.
    /// </summary>
    public HttpPostedFileExtensionsAttribute()
        : base(DataType.Upload)
    {
        ErrorMessage = _innerAttribute.ErrorMessage;
    }

    /// <summary>
    ///     Gets or sets the file name extensions.
    /// </summary>
    /// <returns>
    ///     The file name extensions, or the default file extensions (".png", ".jpg", ".jpeg", and ".gif") if the property is not set.
    /// </returns>
    public string Extensions
    {
        get { return _innerAttribute.Extensions; }
        set { _innerAttribute.Extensions = value; }
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata,
        ControllerContext context)
    {
        var rule = new ModelClientValidationRule
        {
            ValidationType = "extension",
            ErrorMessage = FormatErrorMessage(metadata.GetDisplayName())
        };
        rule.ValidationParameters["extension"] = _innerAttribute.Extensions;
        yield return rule;
    }

    /// <summary>
    ///     Applies formatting to an error message, based on the data field where the error occurred.
    /// </summary>
    /// <returns>
    ///     The formatted error message.
    /// </returns>
    /// <param name="name">The name of the field that caused the validation failure.</param>
    public override string FormatErrorMessage(string name)
    {
        return _innerAttribute.FormatErrorMessage(name);
    }

    /// <summary>
    ///     Checks that the specified file name extension or extensions is valid.
    /// </summary>
    /// <returns>
    ///     true if the file name extension is valid; otherwise, false.
    /// </returns>
    /// <param name="value">A comma delimited list of valid file extensions.</param>
    public override bool IsValid(object value)
    {
        var file = value as HttpPostedFileBase;
        if (file != null)
        {
            return _innerAttribute.IsValid(file.FileName);
        }

        return _innerAttribute.IsValid(value);
    }
}
like image 104
jfeinour Avatar answered Nov 19 '22 14:11

jfeinour


Use the Extensions property to set them. Although according to the documentation

The file name extensions, or the default file extensions (".png", ".jpg", ".jpeg", and ".gif") if the property is not set.

You can set it just like you did the ErrorMessage. The more likely issue is that it doesn't know how to assess whether the HttpPostedFileBase has the right extension. You'll need to use the one from the MVC framework or create your own.

like image 35
Yuriy Faktorovich Avatar answered Nov 19 '22 14:11

Yuriy Faktorovich


I know this is a bit too late, but perhaps this can help someone out there. This is a modified version of @jfeinour, that will work on the client-side as well:

public class HttpPostedFileExtensionAttribute : ValidationAttribute, IClientValidatable {
    private readonly FileExtensionsAttribute _fileExtensionsAttribute = new FileExtensionsAttribute();

    public HttpPostedFileExtensionAttribute() {
        ErrorMessage = _fileExtensionsAttribute.ErrorMessage;
    }

    public string Extensions {
        get { return _fileExtensionsAttribute.Extensions; }
        set { _fileExtensionsAttribute.Extensions = value; }
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) {
        var rule = new ModelClientValidationRule {
            ValidationType = "extension",
            ErrorMessage = FormatErrorMessage(metadata.GetDisplayName())
        };

        rule.ValidationParameters["extension"] =
            _fileExtensionsAttribute.Extensions
                .Replace(" ", string.Empty).Replace(".", string.Empty)
                .ToLowerInvariant();

        yield return rule;
    }

    public override string FormatErrorMessage(string name) {
        return _fileExtensionsAttribute.FormatErrorMessage(name);
    }

    public override bool IsValid(object value) {
        var file = value as HttpPostedFileBase;
        return _fileExtensionsAttribute.IsValid(file != null ? file.FileName : value);
    }
}
like image 36
Dimitar Dimitrov Avatar answered Nov 19 '22 15:11

Dimitar Dimitrov