Asp.net mvc 使用多部分formdata的Web API模型绑定

Asp.net mvc 使用多部分formdata的Web API模型绑定,asp.net-mvc,asp.net-mvc-3,c#-4.0,asp.net-web-api,Asp.net Mvc,Asp.net Mvc 3,C# 4.0,Asp.net Web Api,有没有一种方法可以通过ASP.NET MVC Web API中的多部分表单数据请求获取模型绑定(或其他什么)以提供模型 我看到了各种各样的博客文章,但是在文章和实际版本之间,要么事情发生了变化,要么它们没有显示模型绑定工作 这是一个过时的帖子: 这也是: 我在某个地方发现了这段代码(并做了一些修改),它可以手动读取值: 型号: public class TestModel { [Required] public byte[] Stream { get; set; } [

有没有一种方法可以通过ASP.NET MVC Web API中的多部分表单数据请求获取模型绑定(或其他什么)以提供模型

我看到了各种各样的博客文章,但是在文章和实际版本之间,要么事情发生了变化,要么它们没有显示模型绑定工作

这是一个过时的帖子:

这也是:

我在某个地方发现了这段代码(并做了一些修改),它可以手动读取值:

型号:

public class TestModel
{
    [Required]
    public byte[] Stream { get; set; }

    [Required]
    public string MimeType { get; set; }
}
    public HttpResponseMessage Post()
    {
        if (!Request.Content.IsMimeMultipartContent("form-data"))
        {
            throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
        }

        IEnumerable<HttpContent> parts = Request.Content.ReadAsMultipartAsync().Result.Contents;


        string mimeType;
        if (!parts.TryGetFormFieldValue("mimeType", out mimeType))
        {
            return Request.CreateResponse(HttpStatusCode.BadRequest);
        }

        var media = parts.ToArray()[1].ReadAsByteArrayAsync().Result;

        // create the model here
        var model = new TestModel()
            {
                MimeType = mimeType,
                Stream = media
            };
        // save the model or do something with it
        // repository.Save(model)

        return Request.CreateResponse(HttpStatusCode.OK);
    }
[DeploymentItem("test_sound.aac")]
[TestMethod]
public void CanPostMultiPartData()
{
    var content = new MultipartFormDataContent { { new StringContent("audio/aac"),  "mimeType"}, new ByteArrayContent(File.ReadAllBytes("test_sound.aac")) };

    this.controller.Request = new HttpRequestMessage {Content = content};
    var response = this.controller.Post();

    Assert.AreEqual(response.StatusCode, HttpStatusCode.OK);
}
控制器:

public class TestModel
{
    [Required]
    public byte[] Stream { get; set; }

    [Required]
    public string MimeType { get; set; }
}
    public HttpResponseMessage Post()
    {
        if (!Request.Content.IsMimeMultipartContent("form-data"))
        {
            throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
        }

        IEnumerable<HttpContent> parts = Request.Content.ReadAsMultipartAsync().Result.Contents;


        string mimeType;
        if (!parts.TryGetFormFieldValue("mimeType", out mimeType))
        {
            return Request.CreateResponse(HttpStatusCode.BadRequest);
        }

        var media = parts.ToArray()[1].ReadAsByteArrayAsync().Result;

        // create the model here
        var model = new TestModel()
            {
                MimeType = mimeType,
                Stream = media
            };
        // save the model or do something with it
        // repository.Save(model)

        return Request.CreateResponse(HttpStatusCode.OK);
    }
[DeploymentItem("test_sound.aac")]
[TestMethod]
public void CanPostMultiPartData()
{
    var content = new MultipartFormDataContent { { new StringContent("audio/aac"),  "mimeType"}, new ByteArrayContent(File.ReadAllBytes("test_sound.aac")) };

    this.controller.Request = new HttpRequestMessage {Content = content};
    var response = this.controller.Post();

    Assert.AreEqual(response.StatusCode, HttpStatusCode.OK);
}
这段代码基本上是脆弱的、不可维护的,而且没有强制执行模型绑定或数据注释约束

有更好的方法吗


更新:我看到了这一点,这让我思考-我是否必须为我想要支持的每个模型编写一个新的格式化程序?

这里有一个用于文件上传的通用格式化程序的好例子。如果我要让多个控制器接受文件上传,那么这就是我要采取的方法

另外,环顾四周,这似乎是一个更好的例子,供您在控制器内上传

更新


Re:Multipart方法的有用性,这一点已被涵盖,但实际上,这归结为Multipart方法是为大型二进制有效载荷等精心构建的

默认模型绑定是否正常工作?

WebApi的标准/默认模型绑定器不是为处理您指定的模型而构建的,即混合了简单类型、流和字节数组(不是那么简单)的模型。。。这是一段引自《圣经》的话,启发了lonetechie的创作:

“简单类型”使用模型绑定。复杂类型使用格式化程序。 “简单类型”包括:原语、TimeSpan、DateTime、Guid、, 十进制、字符串或带有类型转换器的东西,从 弦

您在模型上使用字节数组,并且需要从请求的流/内容创建字节数组,这将引导您改用格式化程序

分别发送模型和文件?

就我个人而言,我希望将文件上传与模型分开。。。也许不是你的选择。。。这样,当您使用多部分数据内容类型时,您将发布到相同的控制器和路由,这将调用文件上载格式化程序,当您使用application/json或x-www-form-urlencoded时,它将执行简单的类型模型绑定。。。两个帖子对你来说可能是不可能的,但这是一个选择

自定义模型活页夹?

我有一些小的成功与一个,你可以做一些这也许。。。这可以是通用的(通过一些适度的努力),并且可以在活页夹提供程序中全局注册以供重用

这可能值得一玩

public class Foo
{
    public byte[] Stream { get; set; }
    public string Bar { get; set; }
}

public class FoosController : ApiController
{

    public void Post([ModelBinder(typeof(FileModelBinder))] Foo foo)
    {
        //
    }
}
自定义模型活页夹:

public class FileModelBinder : System.Web.Http.ModelBinding.IModelBinder
{
    public FileModelBinder()
    {

    }

    public bool BindModel(
        System.Web.Http.Controllers.HttpActionContext actionContext,
        System.Web.Http.ModelBinding.ModelBindingContext bindingContext)
    {
        if (actionContext.Request.Content.IsMimeMultipartContent())
        {
            var inputModel = new Foo();

            inputModel.Bar = "";  //From the actionContext.Request etc
            inputModel.Stream = actionContext.Request.Content.ReadAsByteArrayAsync()
                                            .Result;

            bindingContext.Model = inputModel;
            return true;
        }
        else
        {
            throw new HttpResponseException(actionContext.Request.CreateResponse(
             HttpStatusCode.NotAcceptable, "This request is not properly formatted"));
        }
    }
}

@马克·琼斯链接到我的博客文章,这篇文章把我带到了这里。我得想想怎么做你想做的事

我相信,如果您将我的方法与TryValidateProperty()结合起来,您应该能够完成您所需要的。我的方法将反序列化对象,但它不处理任何验证。您可能需要使用反射来循环对象的属性,然后手动调用每个属性上的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=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);
}
}

我仍然不知道如何使用模型绑定或它附带的任何验证。抱歉-我会尽快尝试改进我的答案,但在我这么做之前…您预计这些模型还包含哪些其他属性,它们是否仅与文件上传相关,或者是否会形成实体信息?事先知道属性是正确的确实是我想避免的事情。相反,我希望它做模型绑定,所以是的,我希望它现在有任何模型绑定支持。另外,请参阅我对@particleman的评论。我想知道多部分方法对JSON有效负载的有用性。多部分方法的有用性——好吧,JSON有效负载似乎是如此微不足道如此简单-这是一个不需要动脑筋的问题!那么为什么会有这种多部分的疯狂呢?即使对于更大的文件,什么是d