如何正确实现MediaTypeFormatter来处理'multipart/mixed'类型的请求?

blo*_*aak 21 c#-4.0 asp.net-web-api

考虑使用ASP.NET Web API编写的Web服务将任何数字文件作为"multipart/mixed"请求接受.辅助方法mat看起来如下(假设_client是一个实例System.Net.Http.HttpClient):

public T Post<T>(string requestUri, T value, params Stream[] streams)
{
    var requestMessage = new HttpRequestMessage();
    var objectContent = requestMessage.CreateContent(
        value,
        MediaTypeHeaderValue.Parse("application/json"),
        new MediaTypeFormatter[] {new JsonMediaTypeFormatter()},
        new FormatterSelector());

    var content = new MultipartContent();
    content.Add(objectContent);
    foreach (var stream in streams)
    {
        var streamContent = new StreamContent(stream);
        streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
        streamContent.Headers.ContentDisposition =
            new ContentDispositionHeaderValue("form-data")
            {
                Name = "file",
                FileName = "mystream.doc"
            };
        content.Add(streamContent);
    }

    return _httpClient.PostAsync(requestUri, content)
        .ContinueWith(t => t.Result.Content.ReadAsAsync<T>()).Unwrap().Result;
}
Run Code Online (Sandbox Code Playgroud)

在ApiController的子类中接受请求的方法具有如下签名:

public HttpResponseMessage Post(HttpRequestMessage request)
{
    /* parse request using MultipartFormDataStreamProvider */
}
Run Code Online (Sandbox Code Playgroud)

理想情况下,我想像这样定义它,其中联系人,源和目标是根据'Content-Disposition'标题的'name'属性从'multipart/mixed'内容中提取的.

public HttpResponseMessage Post(Contact contact, Stream source, Stream target)
{
    // process contact, source and target
}
Run Code Online (Sandbox Code Playgroud)

但是,使用我现有的签名,将数据发布到服务器会导致InvalidOperationException出现以下错误消息:

没有'MediaTypeFormatter'可用于读取媒体类型为'multipart/mixed'的'HttpRequestMessage'类型的对象.

互联网上有很多例子说明如何使用ASP.NET Web API和HttpClient发送和接收文件.但是,我还没有发现任何显示如何处理这个问题.

我开始考虑实现自定义MediaTypeFormatter并使用全局配置注册它.然而,虽然在自定义中处理序列化XML和JSON很容易MediaTypeFormatter,但是如何处理"多部分/混合"请求并不清楚,这些请求几乎可以是任何东西.

Jed*_*Jed 14

看看这个论坛:http://forums.asp.net/t/1777847.aspx/1?MVC4+Beta+Web+API+and+multipart+form+data

这是一段代码(由imran_ku07发布),可以帮助您实现自定义格式化程序来处理multipart/form-data:

public class MultiFormDataMediaTypeFormatter : FormUrlEncodedMediaTypeFormatter
{
    public MultiFormDataMediaTypeFormatter() : base()
    {
        this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("multipart/form-data"));
    }

    protected override bool CanReadType(Type type)
    {
        return true;
    }

    protected override bool CanWriteType(Type type)
    {
        return false;
    }

    protected override Task<object> OnReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext)
    {
        var contents = formatterContext.Request.Content.ReadAsMultipartAsync().Result;
        return Task.Factory.StartNew<object>(() =>
        {
            return new MultiFormKeyValueModel(contents);
        });
    }

    class MultiFormKeyValueModel : IKeyValueModel
    {
        IEnumerable<HttpContent> _contents;
        public MultiFormKeyValueModel(IEnumerable<HttpContent> contents)
        {
            _contents = contents;
        }


        public IEnumerable<string> Keys
        {
            get
            {
                return _contents.Cast<string>();
            }
        }

        public bool TryGetValue(string key, out object value)
        {
            value = _contents.FirstDispositionNameOrDefault(key).ReadAsStringAsync().Result;
            return true;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您需要将此格式化程序添加到您的应用程序中.如果做自托管,您可以通过包括以下内容添加它:

config.Formatters.Insert(0, new MultiFormDataMediaTypeFormatter());
Run Code Online (Sandbox Code Playgroud)

在实例化HttpSelfHostServer类之前.

- 编辑 -

要解析二进制流,您需要另一个格式化程序.这是我用来解析我的一个工作项目中的图像的一个.

class JpegFormatter : MediaTypeFormatter
{
    protected override bool CanReadType(Type type)
    {
        return (type == typeof(Binary));
    }

    protected override bool CanWriteType(Type type)
    {
        return false;
    }

    public JpegFormatter()
    {
        SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/jpeg"));
        SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/jpg"));
        SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/png"));
    }

    protected override Task<object> OnReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext)
    {
        return Task.Factory.StartNew(() =>
            {
                byte[] fileBytes = new byte[stream.Length];
                stream.Read(fileBytes, 0, (int)fileBytes.Length);

               return (object)new Binary(fileBytes);
            }); 
    }

    protected override Task OnWriteToStreamAsync(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext, TransportContext transportContext)
    {
        throw new NotImplementedException();
    }
}
Run Code Online (Sandbox Code Playgroud)

在您的控制器/操作中,您需要执行以下操作:

public HttpResponseMessage UploadImage(Binary File) {
 //do something with your file
}
Run Code Online (Sandbox Code Playgroud)