using System; using System.IO; using System.Net.Http; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Reflection; using System.Threading; using System.Threading.Tasks; namespace WebApi.Formatters { public class MultipartFormDataMediaTypeFormatter : MediaTypeFormatter { private const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty; public MultipartFormDataMediaTypeFormatter() { SupportedMediaTypes.Add(new MediaTypeHeaderValue("multipart/form-data")); } public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) { return ReadFromStreamAsync(type, readStream, content, formatterLogger, CancellationToken.None); } public override async Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); try { var obj = Activator.CreateInstance(type); var parts = await content.ReadAsMultipartAsync(cancellationToken); foreach (var item in parts.Contents) { cancellationToken.ThrowIfCancellationRequested(); var partName = item.Headers.ContentDisposition.Name; if (string.IsNullOrEmpty(partName)) { if (parts.Contents.Count == 1) { return await item.ReadAsAsync(type, cancellationToken); } else // multiple parts, no name -> error { formatterLogger.LogError("", "The content part does not have a name"); } } else // part defined its destination { var property = obj.GetType().GetProperty(partName, flags); if (property == null) { formatterLogger.LogError(partName, "The model does not have this property"); } else // we found the property for the name { var part = await item.ReadAsAsync(property.PropertyType, cancellationToken); try { property.SetValue(obj, part); } catch (Exception ex) { formatterLogger.LogError(partName, ex); } } } } return obj; } catch (OperationCanceledException) { throw; } catch (Exception ex) { formatterLogger.LogError("", ex); throw; } } // we can read but cannot write: it would be a much greater project public override bool CanReadType(Type type) { return true; } public override bool CanWriteType(Type type) { return false; } } }