Just to clarify...MediaTypeMapping is not used during the process of reading a request. It is one of the criteria used by the default con-neg algorithm while deciding to
write the response.
The default con-neg algorithm depends on the incoming request's content-type header to decide about the formatter to read. So, in your case you could create a custom formatter which supports the mediatype "application/octect-stream" and add it to the Formatters
collection on the config object.
NOTE: i have a crude example formatter here where i check for the body content to start with "[" and end with "]". The following example is written as a test in XUnit. Hope this helps.
Here Test1 fails as its body is not in expected format, where as Test2 passes.
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Web.Http;
using System.Web.Http.ModelBinding;
using System.Web.Http.SelfHost;
using Xunit;
public class ConNegTests : IDisposable
{
[Fact]
public void Test1()
{
HttpClient httpClient = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
request.Content = new StringContent("Hello");
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/abc");
request.RequestUri = new Uri(this.BaseAddress + "/UpdateData");
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/abc"));
request.Method = HttpMethod.Post;
HttpResponseMessage response = httpClient.SendAsync(request).Result;
Assert.NotNull(response.Content);
Assert.NotNull(response.Content.Headers.ContentType);
Assert.Equal<string>("application/abc", response.Content.Headers.ContentType.MediaType);
Assert.Equal<string>("[Hello-updated]", response.Content.ReadAsStringAsync().Result);
}
[Fact]
public void Test2()
{
HttpClient httpClient = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
request.Content = new StringContent("[Hello]"); //NOTE
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/abc");
request.RequestUri = new Uri(this.BaseAddress + "/UpdateData");
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/abc"));
request.Method = HttpMethod.Post;
HttpResponseMessage response = httpClient.SendAsync(request).Result;
Assert.NotNull(response.Content);
Assert.NotNull(response.Content.Headers.ContentType);
Assert.Equal<string>("application/abc", response.Content.Headers.ContentType.MediaType);
Assert.Equal<string>("[Hello-updated]", response.Content.ReadAsStringAsync().Result);
}
private HttpSelfHostServer server = null;
public ConNegTests()
{
HttpSelfHostConfiguration config = new HttpSelfHostConfiguration(this.BaseAddress);
config.Routes.MapHttpRoute("Default", "{action}", new { controller = "ConNegTests" });
//NOTE
config.Formatters.Add(new AbcFormatter());
config.ServiceResolver.SetService(typeof(IRequestContentReadPolicy), new ReadAsSingleObjectPolicy());
server = new HttpSelfHostServer(config);
server.OpenAsync().Wait();
}
public void Dispose()
{
if (server != null)
{
server.CloseAsync().Wait();
}
}
protected virtual string BaseAddress
{
get { return string.Format("http://{0}:9090/ConNegTests", Environment.MachineName); }
}
}
public class ConNegTestsController : ApiController
{
[HttpPost]
public string UpdateData(string input)
{
return input + "-updated";
}
}
public class AbcFormatter : BufferedMediaTypeFormatter
{
public AbcFormatter()
{
this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/abc"));
}
protected override bool CanReadType(Type type)
{
if (type == typeof(IKeyValueModel))
{
return false;
}
return true;
}
protected override bool CanWriteType(Type type)
{
return true;
}
protected override object OnReadFromStream(Type type, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext)
{
string content = null;
using (var reader = new StreamReader(stream))
{
content = reader.ReadToEnd();
}
if (!content.StartsWith("[") && !content.EndsWith("["))
{
throw new HttpResponseException("Invalid data sent. Expected data to begin with '[' and end with ']'");
}
string trimmedContent = content.TrimStart('[').TrimEnd(']');
return trimmedContent;
}
protected override void OnWriteToStream(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext, TransportContext transportContext)
{
var output = "[" + value.ToString() + "]";
var writer = new StreamWriter(stream);
writer.Write(output);
writer.Flush();
}
}
public class ReadAsSingleObjectPolicy : IRequestContentReadPolicy
{
public RequestContentReadKind GetRequestContentReadKind(System.Web.Http.Controllers.HttpActionDescriptor actionDescriptor)
{
return RequestContentReadKind.AsSingleObject;
}
}
Kiran Challa
Participant
1442 Points
281 Posts
Microsoft
Re: Custom content negotiation
Mar 14, 2012 06:54 PM|LINK
Just to clarify...MediaTypeMapping is not used during the process of reading a request. It is one of the criteria used by the default con-neg algorithm while deciding to write the response.
The default con-neg algorithm depends on the incoming request's content-type header to decide about the formatter to read. So, in your case you could create a custom formatter which supports the mediatype "application/octect-stream" and add it to the Formatters collection on the config object.
NOTE: i have a crude example formatter here where i check for the body content to start with "[" and end with "]". The following example is written as a test in XUnit. Hope this helps. Here Test1 fails as its body is not in expected format, where as Test2 passes.
using System; using System.IO; using System.Net; using System.Net.Http; using System.Net.Http.Formatting; using System.Net.Http.Headers; using System.Web.Http; using System.Web.Http.ModelBinding; using System.Web.Http.SelfHost; using Xunit; public class ConNegTests : IDisposable { [Fact] public void Test1() { HttpClient httpClient = new HttpClient(); HttpRequestMessage request = new HttpRequestMessage(); request.Content = new StringContent("Hello"); request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/abc"); request.RequestUri = new Uri(this.BaseAddress + "/UpdateData"); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/abc")); request.Method = HttpMethod.Post; HttpResponseMessage response = httpClient.SendAsync(request).Result; Assert.NotNull(response.Content); Assert.NotNull(response.Content.Headers.ContentType); Assert.Equal<string>("application/abc", response.Content.Headers.ContentType.MediaType); Assert.Equal<string>("[Hello-updated]", response.Content.ReadAsStringAsync().Result); } [Fact] public void Test2() { HttpClient httpClient = new HttpClient(); HttpRequestMessage request = new HttpRequestMessage(); request.Content = new StringContent("[Hello]"); //NOTE request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/abc"); request.RequestUri = new Uri(this.BaseAddress + "/UpdateData"); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/abc")); request.Method = HttpMethod.Post; HttpResponseMessage response = httpClient.SendAsync(request).Result; Assert.NotNull(response.Content); Assert.NotNull(response.Content.Headers.ContentType); Assert.Equal<string>("application/abc", response.Content.Headers.ContentType.MediaType); Assert.Equal<string>("[Hello-updated]", response.Content.ReadAsStringAsync().Result); } private HttpSelfHostServer server = null; public ConNegTests() { HttpSelfHostConfiguration config = new HttpSelfHostConfiguration(this.BaseAddress); config.Routes.MapHttpRoute("Default", "{action}", new { controller = "ConNegTests" }); //NOTE config.Formatters.Add(new AbcFormatter()); config.ServiceResolver.SetService(typeof(IRequestContentReadPolicy), new ReadAsSingleObjectPolicy()); server = new HttpSelfHostServer(config); server.OpenAsync().Wait(); } public void Dispose() { if (server != null) { server.CloseAsync().Wait(); } } protected virtual string BaseAddress { get { return string.Format("http://{0}:9090/ConNegTests", Environment.MachineName); } } } public class ConNegTestsController : ApiController { [HttpPost] public string UpdateData(string input) { return input + "-updated"; } } public class AbcFormatter : BufferedMediaTypeFormatter { public AbcFormatter() { this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/abc")); } protected override bool CanReadType(Type type) { if (type == typeof(IKeyValueModel)) { return false; } return true; } protected override bool CanWriteType(Type type) { return true; } protected override object OnReadFromStream(Type type, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext) { string content = null; using (var reader = new StreamReader(stream)) { content = reader.ReadToEnd(); } if (!content.StartsWith("[") && !content.EndsWith("[")) { throw new HttpResponseException("Invalid data sent. Expected data to begin with '[' and end with ']'"); } string trimmedContent = content.TrimStart('[').TrimEnd(']'); return trimmedContent; } protected override void OnWriteToStream(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext, TransportContext transportContext) { var output = "[" + value.ToString() + "]"; var writer = new StreamWriter(stream); writer.Write(output); writer.Flush(); } } public class ReadAsSingleObjectPolicy : IRequestContentReadPolicy { public RequestContentReadKind GetRequestContentReadKind(System.Web.Http.Controllers.HttpActionDescriptor actionDescriptor) { return RequestContentReadKind.AsSingleObject; } }Kiran Challa