Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Removing the XML Formatter from ASP.NET Web API Applications


:P
On this page:

ASP.NET Web API's default output format is supposed to be JSON, but when I access my Web APIs using the browser address bar I'm always seeing an XML result instead. When working on AJAX application I like to test many of my AJAX APIs with the browser while working on them. While I can't debug all requests this way, GET requests are easy to test in the browser especially if you have JSON viewing options set up in your various browsers.

If I preview a Web API request in most browsers I get an XML response like this:

XmlView

Why is that?

Web API checks the HTTP Accept headers of a request to determine what type of output it should return by looking for content typed that it has formatters registered for. This automatic negotiation is one of the great features of Web API because it makes it easy and transparent to request different kinds of output from the server.

In the case of browsers it turns out that most send Accept headers that look like this (Chrome in this case):

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Web API inspects the entire list of headers from left to right (plus the quality/priority flag q=) and tries to find a media type that matches its list of supported media types in the list of formatters registered. In this case it matches application/xml to the Xml formatter and so that's what gets returned and displayed.

To verify that Web API indeed defaults to JSON output by default you can open the request in Fiddler and pop it into the Request Composer, remove the application/xml header and see that the output returned comes back in JSON instead.

Fiddler

An accept header like this:

Accept: text/html,application/xhtml+xml,*/*;q=0.9

or leaving the Accept header out altogether should give you a JSON response. Interestingly enough Internet Explorer 9 also displays JSON because it doesn't include an application/xml Accept header:

Accept: text/html, application/xhtml+xml, */*

which for once actually seems more sensible.

Removing the XML Formatter

We can't easily change the browser Accept headers (actually you can by delving into the config but it's a bit of a hassle), so can we change the behavior on the server? When working on AJAX applications I tend to not be interested in XML results and I always want to see JSON results at least during development. Web API uses a collection of formatters and you can go through this list and remove the ones you don't want to use - in this case the XmlMediaTypeFormatter.

To do this you can work with the HttpConfiguration object and the static GlobalConfiguration object used to configure it:

    protected void Application_Start(object sender, EventArgs e)
    {

        // Action based routing (used for RPC calls)
        RouteTable.Routes.MapHttpRoute(
            name: "StockApi",
            routeTemplate: "stocks/{action}/{symbol}",
            defaults: new
            {
                symbol = RouteParameter.Optional,
                controller = "StockApi"
            }
        );

        // WebApi Configuration to hook up formatters and message handlers
        RegisterApis(GlobalConfiguration.Configuration);
    }

    public static void RegisterApis(HttpConfiguration config)
    {
        // remove default Xml handler
        var matches = config.Formatters
                            .Where(f => f.SupportedMediaTypes
                                         .Where(m => m.MediaType.ToString() == "application/xml" ||
                                                     m.MediaType.ToString() == "text/xml")
                                         .Count() > 0)
                            .ToList() ;
        foreach (var match in matches)
            config.Formatters.Remove(match);    
    }
}

That LINQ code is quite a mouthful of nested collections, but it does the trick to remove the formatter based on the content type. You can also look for the specific formatter (XmlMediatTypeFormatter) by its type name which is simpler, but it's better to search for the supported types as this will work even if there are other custom formatters added.

Once removed, now the browser request results in a JSON response:

JsonResponse

It's a simple solution to a small debugging task that's made my life easier. Maybe you find it useful too…

Posted in Web Api  ASP.NET  

The Voices of Reason


 

Alex Henderson
March 08, 2012

# re: Removing the XML Formatter from ASP.NET Web API Applications

For me at least Formatters is something I like to be explicit about (opt in, rather then opt out), so I just do a GlobalConfiguration.Configuration.Formatters.Clear() prior to adding the formatters I want to use (i.e. Json.Net).

tugberk
March 09, 2012

# re: Removing the XML Formatter from ASP.NET Web API Applications

This is similar to how I did that at first till I saw the OOB formatters hanging there. The better way:

http://codebetter.com/glennblock/2012/02/26/disabling-the-xml-formatter-in-asp-net-web-apithe-easy-way-2/

Andreas Gehrke
March 09, 2012

# re: Removing the XML Formatter from ASP.NET Web API Applications

The simple two-liner to only remove the XmlMediaTypeFormatter:

var formatters = GlobalConfiguration.Configuration.Formatters;
formatters.Remove(formatters.XmlFormatter);

Vlad Petrov
March 09, 2012

# re: Removing the XML Formatter from ASP.NET Web API Applications

Hi Rick,

Great approach, simplifies things. A small tip, in RegisterApis method you could use Any(blah) instead of Where(blah).Count > 0

Also maybe it makes sense to have this functionality available only in DEBUG, I'd personally put #if DEBUG / #endif around it.

Cheers
Vlad

Harry M
March 09, 2012

# re: Removing the XML Formatter from ASP.NET Web API Applications

Feels to me like you're approaching this from the wrong direction...

If you are using firefox, you can use this add-in to rewrite headers using custom rules https://code.google.com/p/headertool/ and leave your API intact - not to mention that there are some nice add-ins for viewing the JSON too https://addons.mozilla.org/en-US/firefox/addon/jsonview/

Chris Slatt
March 09, 2012

# re: Removing the XML Formatter from ASP.NET Web API Applications

Or you could

GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();


Which accomplishes the same end result in a slightly different way in one easy to read line of code.

Phil Bolduc
March 09, 2012

# re: Removing the XML Formatter from ASP.NET Web API Applications

Suggestion: replace ".Count() > 0" with ".Any()"

Gene
March 09, 2012

# re: Removing the XML Formatter from ASP.NET Web API Applications

I solved the same problem, but because I still want the option of receiving xml, I added "text/xml" to the Json formatter accept headers. Which allows me to see Json when testing in the browser.


GlobalConfiguration.Configuration.Formatters[0].SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));

Rick Strahl
March 09, 2012

# re: Removing the XML Formatter from ASP.NET Web API Applications

@Chris - clearing all works but you might still want the old JSON serializer in place so you can serialize JsonValue types.

@Gene - Excellent tip to add text/xml to the media type headers!

Eric Falsken
December 10, 2012

# re: Removing the XML Formatter from ASP.NET Web API Applications

I actually do something entirely different. Using this line lets me register my custom FormattingOverrideFilter.

// This method allows the client to override their browser's HTTP Accept header by putting a value in the querystring.
GlobalConfiguration.Configuration.Filters.Add(new FormattingOverrideFilterAttribute(formatPreferenceOverride: "application/json", clearOtherPreferredMediaTypes: true));
 
...
 
public class FormattingOverrideFilterAttribute : ActionFilterAttribute{
    /// <summary>
    /// Allows overriding the Accept header by querystring or server preference.
    /// </summary>
    /// <param name="formatPreferenceOverride">If defined, this mime type will be injected into the Accept header of every request.</param>
    /// <param name="newQuality">Optional, The quality setting for this media type. Should be between 1 and 0.</param>
    /// <param name="clearOtherPreferredMediaTypes">If true, will remove all other media types from the Accept header.</param>
    public FormattingOverrideFilterAttribute(string formatPreferenceOverride = null, double? newQuality = null, bool clearOtherPreferredMediaTypes = false) : base() {
        NewQualityValue = newQuality;
        FormatPreferenceOverride = formatPreferenceOverride;
        ClearOtherPreferredMediaTypes = clearOtherPreferredMediaTypes;
    }
 
    public double? NewQualityValue { get; set; }
    public string FormatPreferenceOverride { get; set; }
    public bool ClearOtherPreferredMediaTypes { get; set; }
 
    public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) {
        var qs = actionContext.Request.RequestUri.ParseQueryString();
            
        var requestedMediaType = (qs["f"] ?? qs["format"] ?? FormatPreferenceOverride ?? String.Empty).ToLower();
 
        switch (requestedMediaType) {
            case "xml":
            case "x":
                requestedMediaType = "application/xml";
                break;
            case "j":
            case "js":
            case "json":
                requestedMediaType = "application/json";
                break;
        }
 
        if (String.IsNullOrEmpty(requestedMediaType))
            return; // No action is necessary because no override was requested.
 
        var acceptHeaders = actionContext.Request.Headers.Accept;
        MediaTypeWithQualityHeaderValue header = null;
 
        if (ClearOtherPreferredMediaTypes) {
            var newAcceptHeaders = acceptHeaders.Where(h => h.MediaType == requestedMediaType || h.MediaType == "*/*").ToList();
            acceptHeaders.Clear();
            newAcceptHeaders.ForEach(acceptHeaders.Add);
        }
 
        header = acceptHeaders.FirstOrDefault(h => h.MediaType == requestedMediaType);
        if (header == null) {
            header = new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue(requestedMediaType);
            acceptHeaders.Add(header);
        }
 
        System.Diagnostics.Debug.WriteLine("Overidding HttpAccept header for format preference.");
        header.Quality = NewQualityValue;
    }
}

Nagareddy S. Reddy
January 10, 2014

# re: Removing the XML Formatter from ASP.NET Web API Applications

public static void Register(HttpConfiguration config)
{
// Web API configuration and services

// Web API routes
config.MapHttpAttributeRoutes();

config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);

======> config.Formatters.Remove(config.Formatters.XmlFormatter);

The Above one liner does the job. // this defaults to JSON formatting.

}

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024