Change Response Headers on Media Type Formatter for ASP.NET Web API

43,158

When you write to stream in the formatter, headers have been already sent.

You can do this:

    public HttpResponseMessage Get(string id) {
    {
        var value = new iOSDevice();
        var response = Request.CreateResponse();
        response.Content = new ObjectContent(typeof(iOSDevice), value, new iOSDeviceXmlFormatter());
        //set headers on the "response"
        return response;
    }

or you can do this (add this method to your formatter):

    public override void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, string mediaType)
    {
        base.SetDefaultContentHeaders(type, headers, mediaType);
        headers.ContentType = new MediaTypeHeaderValue("application/xml");
    }

Here is an example on how I used the SetDefaultContentHeaders with a custom formatter: http://www.strathweb.com/2012/09/generate-kindle-mobi-ebooks-with-your-asp-net-web-api/

   public override void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType)
   {
      if (CanWriteType(type) && mediaType.MediaType == supportedMediaType)
      {
         headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
         headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
         headers.ContentDisposition.FileName = "ebook.mobi";
      }
      else
      {
         base.SetDefaultContentHeaders(type, headers, mediaType);
      }
   }
Share:
43,158
Allison A
Author by

Allison A

Updated on January 08, 2020

Comments

  • Allison A
    Allison A over 4 years

    I have created an ASP.NET web API controller that is returning a strongly typed object on an action, as follows:

    // GET api/iosdevices/5
    public iOSDevice Get(string id) {
      return new iOSDevice();
    }
    

    I have created a BufferedMediaTypeFormatter to handle the type iOSDevice:

    public class iOSDeviceXmlFormatter : BufferedMediaTypeFormatter
    {
        public iOSDeviceXmlFormatter() {
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/xml"));
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
        }
    
        public override void WriteToStream(Type type, object value, Stream writeStream, HttpContent content) {
            content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
            iOSDevice device = (iOSDevice)value;
            using (XmlWriter writer = XmlWriter.Create(writeStream)) {
                writer.WriteStartElement("iOSDevice");
                if (device.identifierForVendor != Guid.Empty) {
                    writer.WriteElementString("identifierForVendor", device.identifierForVendor.ToString());
                    writer.WriteElementString("userInterfaceIdiom", device.userInterfaceIdiom);
                    writer.WriteElementString("systemName", device.systemName);
                    writer.WriteElementString("systemVersion", device.systemVersion);
                    writer.WriteElementString("model", device.model);
                }
                writer.WriteEndElement();
            }
            writeStream.Close();
        }
    }
    

    My problem is when I catch type "text/html" (e.g. someone attempts to access the API from his or her web browser), the response type is "text/html" instead of "application/xml". I want to override the response type so that the user gets a response that is "application/xml" instead of "text/html".

    I cannot in the ApiController type get access to the "Response" property that is on regular MVC controllers and I am at a loss. How do I override the response type for this action that is being handled by a media type formatter?

    EDIT: HELPFUL NOTE

    I was trying this previously:

    var response = Request.CreateResponse<iOSDevice>(HttpStatusCode.Accepted, device);
    response.Headers.Remove("Content-Type");
    response.Headers.Add("Content-Type", "application/xml; charset=utf-8");
    return response;
    

    And it claimed I was "misusing" the headers.

    But when I used Filip's example below of setting Content directly, it worked!

    var response = Request.CreateResponse();
    response.Content = new ObjectContent<iOSDevice>(device, new iOSDeviceXmlFormatter());
    return response;
    
  • Allison A
    Allison A over 11 years
    Brilliant! It worked! However... it didnt like this: var response = Request.CreateResponse<iOSDevice>(HttpStatusCode.Accepted, device); response.Headers.Remove("Content-Type"); response.Headers.Add("Content-Type", "application/xml; charset=utf-8"); return response; I had to do it the way you said to, with setting the Content property. Thank you!!
  • THX-1138
    THX-1138 almost 9 years
    Follow up question. Is it possible to change file name from within formatter, so that file name is specific to each request, rather than set generally to "ebook.mobi"?