Returning Stream with custom media formater appends XML serialization of Stream object to response

Topics: jQuery, Web Api
Jul 15, 2011 at 4:18 PM

Right now I am stuck with this problem and hopefully someone has an idea.

I have a simple method returning a javascript as stream:

[WebInvoke(Method = "GET", UriTemplate = "/script.js")]
public Stream GetJavascript()
{
  StringBuilder sb = new StringBuilder();
  sb.Append("alert('hello world');");
  return new MemoryStream(Encoding.UTF8.GetBytes(sb.ToString()));
}

My media formatter simply returns the stream content with type text/savascript:

public class JavaScriptMediaTypeFormatter : MediaTypeFormatter
{
  public JavaScriptMediaTypeFormatter() : base()
  {            
    this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/javascript"));
  }
  protected override bool OnCanReadType(Type type)
  {            
    return false;        
  }        
  
  protected override bool OnCanWriteType(Type type)        
  {
    return typeof(Stream) == type;        
  }         
  
  public override void OnWriteToStream(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, TransportContext context)        
  {
    contentHeaders.ContentType = new MediaTypeHeaderValue("text/javascript");
    var data = value as Stream;
    if (null == data)
    {
      contentHeaders.ContentLength = 0;
      return;
    }
    contentHeaders.ContentLength = data.Length;
    byte[] buffer = new byte[16 * 1024];
    int read;        
    while ((read = data.Read(buffer, 0, buffer.Length)) > 0)        
    { 
      stream.Write(buffer, 0, read);
    }
  }
}

The formatter is added to the response handlers on my custom operations handler factory:

public class WebOperationHandlerFactory : HttpOperationHandlerFactory
{
  ...
  protected override Collection<HttpOperationHandler>  OnCreateResponseHandlers(
 ServiceEndpoint endpoint,HttpOperationDescription operation)
  {
    var handlers = base.OnCreateResponseHandlers(endpoint, operation);
    foreach (var handler in handlers)
    {
      if (handler is ResponseContentHandler)
      {
        ((ResponseContentHandler)handler).Formatters.Add(new JavaScriptMediaTypeFormatter());
      }
    }
    return handlers;
  }
}

The whole thing is wired up by a custom service host factory:

public class MyHttpServiceHostFactory : HttpConfigurableServiceHostFactory
{
  public MyHttpServiceHostFactory(IHttpHostConfigurationBuilder builder) : base(CreateConfiguration())
  {
  }
  private static IHttpHostConfigurationBuilder CreateConfiguration()
  {
    var config = HttpHostConfiguration.Create()
 .SetOperationHandlerFactory<WebOperationHandlerFactory>();
    config.Configuration.AddFormatters(new JavaScriptMediaTypeFormatter());
    config.Configuration.AddMessageHandlers(typeof(UriFormatExtensionMessageChannel));
   return config;
}
...

The UriFormatExtensionMessageChannel overrides the SendAsync() method and performs:

request.Headers.Accept.Clear();
request.Headers.Accept.Add(mediaType);

where mediaType is "text/javascript"

The route to the service is set as:

routes.Add(new ServiceRoute("resource",
 new MyHttpServiceHostFactory("https"), typeof(ScriptAPI)));

So far this works fine but the result calling the method is:


alert('hello world');
<?xml version="1.0" encoding="utf-8"?><Stream p1:nil="true" xmlns:p1="http://www.w3.org/2001/XMLSchema-instance" />

Why is the Stream object serialized and appened to the output?

Thanks for your help!


Marcus

 

Jul 15, 2011 at 5:39 PM

I guess I made things much to complicated in this case. Sending as HttpResponseMessage with the script was all that is needed here:

[WebInvoke(Method = "GET", UriTemplate = "/script.js")]
public HttpResponseMessage GetJavascript(HttpRequestMessage request)
{
var response = new HttpResponseMessage();
response.Content = new StringContent("alert('hello world');", 
  Encoding.UTF8, "text/javascript");
response.StatusCode = HttpStatusCode.OK;
return response;
}
Anyhow, I would be very interested in knowing why the above does not work. Maybe there is something with the whole formatter thing I did not understand.