Asp.net mvc Web API模型绑定器不';无法使用HttpPostedFileBase?

Asp.net mvc Web API模型绑定器不';无法使用HttpPostedFileBase?,asp.net-mvc,asp.net-web-api,Asp.net Mvc,Asp.net Web Api,测试用于文件上传的Web API,使用如下简单视图模型: public class TestModel { public string UserId {get;set;} public HttpPostedFileBase ImageFile {get;set;} } 在方法中使用: [HttpPost] public void Create(TestModel model) 当我尝试将多部分/表单数据编码表单发布到操作时,我收到以下异常: System.InvalidOpe

测试用于文件上传的Web API,使用如下简单视图模型:

public class TestModel {
    public string UserId {get;set;}
    public HttpPostedFileBase ImageFile {get;set;}
}
在方法中使用:

[HttpPost]
public void Create(TestModel model)
当我尝试将多部分/表单数据编码表单发布到操作时,我收到以下异常:

System.InvalidOperationException: No MediaTypeFormatter is available to read an object of type 'TestModel' from content with media type 'multipart/form-data'.
   at System.Net.Http.HttpContentExtensions.ReadAsAsync[T](HttpContent content, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger)
   at System.Net.Http.HttpContentExtensions.ReadAsAsync(HttpContent content, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger)
   at System.Web.Http.ModelBinding.FormatterParameterBinding.ReadContentAsync(HttpRequestMessage request, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger)
   at System.Web.Http.ModelBinding.FormatterParameterBinding.ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
   at System.Web.Http.Controllers.HttpActionBinding.<>c__DisplayClass1.<ExecuteBindingAsync>b__0(HttpParameterBinding parameterBinder)
   at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
   at System.Threading.Tasks.TaskHelpers.IterateImpl(IEnumerator`1 enumerator, CancellationToken cancellationToken) 
System.InvalidOperationException:没有MediaTypeFormatter可用于从媒体类型为“multipart/form data”的内容中读取类型为“TestModel”的对象。
位于System.Net.Http.HttpContentExtensions.ReadAsAsync[T](HttpContent内容,类型类型,IEnumerable`1格式化程序,IFormatterLogger格式化程序记录器)
位于System.Net.Http.HttpContentExtensions.ReadAsAsync(HttpContent内容,类型,IEnumerable`1格式化程序,IFormatterLogger格式化程序记录器)
位于System.Web.Http.ModelBinding.FormatterParameterBinding.ReadContentAsync(HttpRequestMessage请求,类型类型,IEnumerable`1格式化程序,IFormatterLogger格式化程序记录器)
位于System.Web.Http.ModelBinding.FormatterParameterBinding.ExecuteBindingAsync(ModelMetadataProvider metadataProvider,HttpActionContext actionContext,CancellationToken CancellationToken)
在System.Web.Http.Controllers.HttpActionBinding.c__DisplayClass1.b__0(HttpParameterBinding parameterBinder)
在System.Linq.Enumerable.WhereSelectArrayInterator`2.MoveNext()中
位于System.Threading.Tasks.TaskHelpers.IterateImpl(IEnumerator`1枚举器,CancellationToken CancellationToken)

这适用于默认的MVC模型绑定器,但显然不适用于Web API。发现有人提到上传文件时不能使用视图模型,只需将数据分成两个调用。这对我不起作用,因为我需要发布其他字段,以便实际处理上传的文件。有没有办法做到这一点?

您可以编写一个自定义的
MediaTypeFormatter
来简化您的场景,也可以使用
MultipartFormDataStreamProvider.FormData.AllKeys
集合手动从请求中提取数据。通过这种方式,您可以在一个请求中发布文件和其他字段

Mike Wasson的一个很好的教程可以在这里找到:

查看我的原始答案

基本上,将我在博客文章中的方法和TryValidateProperty()建议结合起来,以维护模型验证注释

编辑: 我继续在博客文章中对我的代码进行了代码增强。我将很快发布这个更新的代码。下面是一个简单的示例,它验证每个属性,并允许您访问结果数组。只是一种方法的示例

public class FileUpload<T>
{
    private readonly string _RawValue;

    public T Value { get; set; }
    public string FileName { get; set; }
    public string MediaType { get; set; }
    public byte[] Buffer { get; set; }

    public List<ValidationResult> ValidationResults = new List<ValidationResult>(); 

    public FileUpload(byte[] buffer, string mediaType, string fileName, string value)
    {
        Buffer = buffer;
        MediaType = mediaType;
        FileName = fileName.Replace("\"","");
        _RawValue = value;

        Value = JsonConvert.DeserializeObject<T>(_RawValue);

        foreach (PropertyInfo Property in Value.GetType().GetProperties())
        {
            var Results = new List<ValidationResult>();
            Validator.TryValidateProperty(Property.GetValue(Value),
                                          new ValidationContext(Value) {MemberName = Property.Name}, Results);
            ValidationResults.AddRange(Results);
        }
    }

    public void Save(string path, int userId)
    {
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
        }
        var SafeFileName = Md5Hash.GetSaltedFileName(userId,FileName);
        var NewPath = Path.Combine(path, SafeFileName);
        if (File.Exists(NewPath))
        {
            File.Delete(NewPath);
        }

        File.WriteAllBytes(NewPath, Buffer);

        var Property = Value.GetType().GetProperty("FileName");
        Property.SetValue(Value, SafeFileName, null);
    }
}
公共类文件上传
{
私有只读字符串_RawValue;
公共T值{get;set;}
公共字符串文件名{get;set;}
公共字符串媒体类型{get;set;}
公共字节[]缓冲区{get;set;}
公共列表验证结果=新列表();
公共文件上载(字节[]缓冲区、字符串媒体类型、字符串文件名、字符串值)
{
缓冲区=缓冲区;
MediaType=MediaType;
FileName=FileName.Replace(“\”,“”);
_原始价值=价值;
Value=JsonConvert.DeserializeObject(_RawValue);
foreach(Value.GetType().GetProperties()中的PropertyInfo属性)
{
var Results=新列表();
Validator.TryValidateProperty(Property.GetValue(Value),
新的ValidationContext(值){MemberName=Property.Name},结果);
ValidationResults.AddRange(结果);
}
}
公共void保存(字符串路径,int userId)
{
如果(!Directory.Exists(path))
{
CreateDirectory(路径);
}
var SafeFileName=Md5Hash.GetSaltedFileName(userId,FileName);
var NewPath=Path.Combine(路径,安全文件名);
if(File.Exists(NewPath))
{
删除(NewPath);
}
writealBytes(新路径,缓冲区);
var Property=Value.GetType().GetProperty(“文件名”);
SetValue(值,安全文件名,null);
}
}

您需要编写一个自定义的
MediaTypeFormatter
才能使其正常工作。正如您所经历的那样,“多部分/表单数据”是不支持开箱即用的。您可以在阅读本文后开始这样做,我知道如何做到这一点。但是,除非我有误解,否则仍然无法使用模型绑定器(以及随之而来的各种数据注释)。因此,我们现在必须手动验证所有输入?您是否碰巧知道为什么在模型绑定器中需要进行此更改?它非常有效地打破了能够将现有模型用于API项目的概念,并且似乎是一个非常奇怪的方向。