Web API模型绑定与多部分formdata

有没有一种方法能够从ASP.NET MVC Web API中的多部分表单数据请求中获取模型绑定(或其他)以发布模型?

我看到各种博客帖子,但是发布和实际发布之间的事情都发生了变化,或者他们没有显示模型绑定的工作。

这是一个过时的文章:发送HTML表单数据

使用ASP.NET 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);
}

这段代码基本上是脆弱的,不可维护的,而且不会强制执行模型绑定或数据注释约束。

有一个更好的方法吗?

更新:我看过这篇文章,这让我想 - 我必须为每一个我想支持的模型写一个新的格式化程序吗?


@Mark琼斯链接到我的博客文章http://lonetechie.com/2012/09/23/web-api-generic-mediatypeformatter-for-file-upload/这导致我在这里。 我不得不考虑如何去做你想做的事。

我相信如果你把我的方法和TryValidateProperty()结合起来,你应该能够完成你所需要的。 我的方法会得到一个反序列化的对象,但它不处理任何验证。 您可能需要使用反射来遍历对象的属性,然后手动调用每个对象的TryValidateProperty()。 这种方法是多一点,但我不知道该怎么做。

http://msdn.microsoft.com/en-us/library/dd382181.aspx http://www.codeproject.com/Questions/310997/TryValidateProperty-not-work-with-generic-function

编辑:其他人问这个问题,我决定编写它只是为了确保它的工作。 这是我的博客更新后的验证检查代码。

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);
    }
}

这里有一个用于文件上传的通用格式化程序的好例子http://lonetechie.com/2012/09/23/web-api-generic-mediatypeformatter-for-file-upload/。 如果我打算让多个控制器接受文件上传,那么这将是我会采取的方法。

PS环顾四周,看起来好像是你在控制器中上传的更好的例子http://www.strathweb.com/2012/08/a-guide-to-asynchronous-file-uploads-in-asp-net-web- API-RTM /

更新

Re:多部分方法的用处 ,这里已经介绍了这一点,但是有效地归结为多部分方法可以很好地构建大尺寸的二进制有效载荷等。

DEFAULT模型绑定是否正常工作?

WebApi的标准/默认模型绑定器不是为了处理您指定的模型而构建的,也就是将简单类型和流和字节数组混合在一起(不是那么简单)......这是来自激发lonetechie的文章的引用:

“简单类型”使用模型绑定。 复杂类型使用格式化程序。 一个“简单类型”包括:原始类型,TimeSpan,DateTime,Guid,Decimal,String或带有TypeConverter的东西,可以从字符串转换

您在模型中使用字节数组并需要从请求的流/内容创建该数组,而不是直接使用格式化程序。

分别发送模型和文件?

就我个人而言,我会寻求从模型中分离文件上传...可能不是您的选择...这样,当您使用MultiPart数据内容类型时,您将POST到相同的控制器和路由,这将调用文件上传格式化程序并且当你使用application / json或者x-www-form-urlencoded的时候,它会做简单的类型模型绑定......两个POST对你来说可能是不可能的,但它是一个选项......

自定义模型绑定器

我用一个自定义模型绑定器取得了一些小小的成功,你可以用这个做一些事情......这可以做成通用的(有一些适中的努力),并且可以在活页夹提供程序中全局注册以便重用......

这可能值得一玩吗?

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"));
        }
    }
}
链接地址: http://www.djcxy.com/p/22153.html

上一篇: Web API Model Binding with Multipart formdata

下一篇: How to prepare encoded POST data on javascript?