FileExtensions属性 - 使用Enumerator作为参数

时间:2017-10-26 16:00:45

标签: c# .net-core validationattribute

长话短说 - 我有Entity Framework模型接受Enum类型属性:

public class FileUploads {
    public AllowedFileTypes FileType { get; set; }
}

Enum是:

public enum AllowedFileTypes {
    jpg,
    png,
    gif,
    jpeg,
    bmp,
}

然后,在Web API控制器中,我为IFormFile设置了验证属性,如下所示:

[HttpPost]
public async Task<IActionResult> Upload(
    [Required]
    [FileExtensions(Extensions = "jpg,png,gif,jpeg,bmp")] // allowed filetypes
    IFormFile file)
{
    return Ok()
}

该方法用于上传文件。现在,问题是我基本上手动设置FileExtensions属性允许的格式。这意味着,将来每当将新文件格式添加到枚举时,我都需要手动更新每个FileExtensions属性。这很容易被遗忘,或者任何其他开发人员都不会意识到这一点......

所以,我在考虑是否可以将Enum类型参数传递给FileExtensions属性?

我的尝试如下:

[FileExtensions(Extensions = string.Join(",", Enum.GetValues(typeof(FileExtensions))))]

不幸的是,Extensions参数必须是const类型字符串,因此VS会引发错误。我当然可以编写自己的自定义验证属性,例如:

FileExtensions fileExtension;
bool fileExtensionParseResult = Enum.TryParse<FileExtensions>(Path.GetExtension(file.FileName), true, out fileExtension);

还有其他想法吗?

1 个答案:

答案 0 :(得分:2)

因此,当我处理白名单时,我通常使用配置文件而不是硬编码到应用程序中。此外,我将利用Content-Type标头来确定请求的内容类型。他们应该在上传jpg时发送像image / jpeg这样的东西。

如果这不能为您提供足够的信息,请发表评论,我会做一个简单的例子。

编辑:

这是我自己项目的一个例子。在appsettings.json中,添加以下内容:

"AllowedFileUploadTypes": {
    "image/jpeg": "jpg",
    "video/quicktime": "mov"
  }

我通常会创建一个用于访问设置的包装类,下面是我的.NET Core版本的示例:

using System.Linq;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;

public class AppConfigurationManager
{
    private IConfiguration _configuration;

    public AppConfigurationManager(IConfiguration configuration)
    {
        _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
    }

    public IDictionary<string, string> AllowedFileUploadTypes =>
                    _configuration.GetSection(nameof(AllowedFileUploadTypes)).GetChildren()
                        .Select(item => new KeyValuePair<string, string>(item.Key, item.Value))
                        .ToDictionary(x => x.Key, x => x.Value);

}

当然你必须在Startup.cs中注册它

public class Startup
{
    public Startup(IConfiguration configuration)
    {            
        Configuration = configuration;

    }
    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        //stuff...
        services.AddSingleton(Configuration);
        services.AddSingleton<AppConfigurationManager>();
        //other stuff...
    }
}

然后,您可以使用AppConfigurationManager.AllowedFileUploadTypes来评估IFormFile.ContentType属性,以验证文件的内容类型是否有效。您可以尝试从字典中获取值,然后针对该属性进行验证。根据文档,我假设将使用Content-Type标头填充ContentType属性。我通常使用块上传文件,所以我没有使用IFormFile

编辑:想要一种方法来应用行动。

使用ActionFilterAttribute,您可以执行以下操作:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

public class ValidateFileExtensionsAttribute : ActionFilterAttribute
{

    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        var fileKeyValue = context.ActionArguments.FirstOrDefault(x => typeof(IFormFile).IsAssignableFrom(x.Value.GetType()));

        if (fileKeyValue.Value != null)
        {
            AppConfigurationManager sessionService = context.HttpContext.RequestServices.GetService(typeof(AppConfigurationManager)) as AppConfigurationManager;
            IFormFile fileArg = fileKeyValue.Value as IFormFile;

            if (!sessionService.AllowedFileUploadTypes.Keys.Any(x => x == fileArg.ContentType))
            {
                context.Result = new ObjectResult(new { Error = $"The content-type '{fileArg.ContentType}' is not valid." }) { StatusCode = 400 };

                //or you could set the modelstate
                //context.ModelState.AddModelError(fileKeyValue.Key, $"The content-type '{fileArg.ContentType}' is not valid.");
                return;
            }
        }

        await next();
    }
}

然后你可以将它应用于这样的动作:

[HttpPost]
[ValidateFileExtensions]
public async Task<IActionResult> Upload([Required]IFormFile file)
{
    return Ok();
}

您可以修改ActionFilter以设置ModelState,也可以只返回值。

〜干杯