Skip to content

Instantly share code, notes, and snippets.

@quexy
Created June 19, 2017 11:16
Show Gist options
  • Save quexy/2f977be255074835607a3deca2b28c9c to your computer and use it in GitHub Desktop.
Save quexy/2f977be255074835607a3deca2b28c9c to your computer and use it in GitHub Desktop.
WebAPI 'multipart/form-data' input formatter
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; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment