Recently I have been writing a WebApi project which needs to accept plaintext via the body of a PUT request, and did the logical thing of using the FromBodyAttribute
public HttpStatusCode PutKv([FromBody]string content, string keyGreedy)
{
return HttpStatusCode.OK;
}
Which didn’t work, with the useful error message of “Unsupported media type.”
It turns out that to bind a value type with the FromBody
attribute, you have to prefix the body of your request with an =
. As I am emulating another Api’s interface, this is not an option, so I set about figuring out how to override this requirement.
In the end I discovered that providing a new MediaTypeFormatter
which handles plaintext is the answer:
public class PlainTextMediaTypeFormatter : MediaTypeFormatter
{
public PlainTextMediaTypeFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain"));
}
public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
{
var source = new TaskCompletionSource<object>();
try
{
using (var memoryStream = new MemoryStream())
{
readStream.CopyTo(memoryStream);
var text = Encoding.UTF8.GetString(memoryStream.ToArray());
source.SetResult(text);
}
}
catch (Exception e)
{
source.SetException(e);
}
return source.Task;
}
public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, System.Net.TransportContext transportContext, System.Threading.CancellationToken cancellationToken)
{
var bytes = Encoding.UTF8.GetBytes(value.ToString());
return writeStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
}
public override bool CanReadType(Type type)
{
return type == typeof(string);
}
public override bool CanWriteType(Type type)
{
return type == typeof(string);
}
}
This can then be added to the config.Formatters
collection:
public static class WebApiConfig
{
public static void Register(HttpConfiguration http)
{
http.Formatters.Add(new PlainTextMediaTypeFormatter());
}
}
It really seems like something which should be supplied out of the box with WebApi to me, but at least it wasn’t as complicated to implement as I was expecting it to be :)