Consider only adding the JSON formatter by default #1765

Closed
danroth27 opened this Issue Jan 6, 2015 · 29 comments

Projects

None yet
@danroth27
Member

Not everyone wants XML support for their Web APIs. In general it is better to add something than to have to remove it. Should we only have the JSON formatter by default? Should we factor out the XML support into its own package?

As an example of when having XML enabled by default causes problems consider this StackOverflow thread where a bunch of folks would prefer to see JSON by default when using Chrome to test their Web API:
http://stackoverflow.com/questions/9847564/how-do-i-get-asp-net-web-api-to-return-json-instead-of-xml-using-chrome.

See further discussion here: aspnet/Home#245.

@MMrR0b0TT

I'd say that is a good idea. I have been changing this everytime I start a new project and sometimes I receive some questions about this "issue". Change it, please!

@super-hype

Its really a good idea. Json is a kind of standard now... Just put some configuration for those who needs XML.

@stanislavromanov
Contributor

It would be nice if we could just use JSON as default and leave XML as secondary. Right now it's opposite.

@ctolkien
ctolkien commented Jan 6, 2015

+1 on having JSON formatter by default.

@JamesNK
Contributor
JamesNK commented Jan 7, 2015

The first thing I do with a blank Web API project is add:

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

I have no opinion on putting the XML formatter in its own assembly but +1 to remove it from the configured formatters by default.

@spboyer
spboyer commented Jan 7, 2015

JSON should be the default and having the option to add the XML formatter would be the preferred via nuget package.

XML by default was there probably due to the fact that WebAPI came from WCF with SOAP + XML being the primary transport.

@cmatskas
cmatskas commented Jan 7, 2015

JSON default +1, XML optional

@khalidabuhakmeh

👍

@LucasMoffitt

Json should be the default with XML being very optional.

@paulirwin

+1 for JSON being default.

@davymac23

+1 for JSON by default - XML as an option

@nvivo
nvivo commented Jan 7, 2015

Just for some clarification on the issue: JSON is currently the default. If you create a request without any "Accept" headers, web api seems to return json (probably because it is registered first?)

The issue is that all major browsers except IE request specifically for "application/xml" by default if you do a request from the url bar. Asp.net has content negotiation, so it will try to honor the request and selects the Xml formatter (because it is registered). So, ASP.NET is technically doing the right thing.

The discussion is just if these lines should be there by default (and the InputFormatter counterpart).

Now, I also have some serious opinions on whether content negotiation between xml and json by default is a good idea. Simple examples will always work, but in practice it is ridiculously easy to make things break with valid code. Consider this code:

public object Get()
{
    return new
    {
        count = 3,
        records = new[] { 1, 2, 3 }
    };
}

This is a very common pattern for a web api + javascript, and the xml serializer will just blow up here complaining there is no DataContractAttribute in the anonymous object, while Json just works.

There are also differences in how both serializers handle things that cause different behavior in applications using json vs xml. Look at this, using a empty web project with latest web api, default settings:

public class SomeClass
{
    public SomeClass()
    {
        A = new List<int> { 1, 2 };
        B = new List<int> { 1, 2 };
    }

    public List<int> A { get; set; }
    public List<int> B { get; private set; }
}

public class ValuesController : ApiController
{
    public SomeClass Get()
    {
        return new SomeClass();
    }

    public SomeClass Post([FromBody] SomeClass x)
    {
        return x;
    }
}

Now, just get the values with different headers:

GET /api/values
Accept: application/json
{ "A": [1, 2], "B": [1, 2] }

Same request with XML:

GET /api/values
Accept: application/xml
<SomeClass>
  <A>
   <d2p1:int>1</d2p1:int>
   <d2p1:int>2</d2p1:int>
  </A>
</SomeClass>

The default xml serializer ignores read only properties, so there is no "B" there, causing different client behavior.

Now for some issues in the input serialization:

POST /api/values
Accept: application/json
Content-type: application/json
{ "A": [3, 4] }

Response:

{ "A": [1, 2, 3, 4], "B": [1, 2] }

And with XML:

POST /api/values
Accept: application/xml
Content-type: application/xml
<SomeClass xmlns="http://schemas.datacontract.org/2004/07/WebApplication1.Controllers">
 <A xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
  <d2p1:int>3</d2p1:int>
  <d2p1:int>4</d2p1:int>
 </A>
</SomeClass>
<SomeClass>
 <A>
   <d2p1:int>3</d2p1:int>
   <d2p1:int>4</d2p1:int>
 </A>
</SomeClass>

Here, JSON.NET by default merges lists while the xml serializer replaces it. Those are just some things I found very quickly in real world apps.

Some of these settings could even be tuned by default to have similar results (causing a lot of breaking changes), but is it worth the hassle?

By having both serializers by default we guarantee there is a huge number of ASP.NET websites out there that have never been properly tested and have broken APIs, as a poor showcase for the platform.

I believe that having content negotiation in an application is not that trivial, and is just as internationalization: to make it work, you need to develop and test your application for that.

The simple solution is just have a single protocol (json) by default and if you need two formats, you enable it and deal with the consequences just like supporting two languages.

@stanislavromanov
Contributor

but it is worth the hassle?

I agree that it's not worth it.

have broken APIs, as a poor showcase for the platform

Also agree with this.

@danroth27
Member

Yes, @nvivo is correct that the JSON formatter is already the default formatter and will be selected if the client doesn't request a different format using the Accept header. The default formatter is the first one in the list. The question here is really should the JSON formatter be the only formatter enabled by default.

I'm hearing two arguments in favor of this change:

  1. Folks want to see JSON when browsing to their Web APIs
  2. Enabling XML along with JSON imposes additional test overhead for functionality that many Web APIs don't need.

Are there other reasons folks don't want XML enabled by default?

You should be able to get a consistent behavior between the XML and JSON formatters if you stick to the Data Contract programming model, but it sounds like this doesn't cover some common cases. I've filed #1777 to track this issue separately.

@JamesNK
Contributor
JamesNK commented Jan 7, 2015

I think the third reason to not have XML enabled is does your average developer understand that content negotiation is even a thing? Do they need to understand it is a thing? By having the JSON and XML formatter's, and side effects like these, you're forcing them to learn.

On the other hand if you only have the JSON formatter and always return JSON then Joe Average can develop on in happy ignorance, and if they need to worry about multiple formats then they can discover content negotiation, and add additional formatters.

JSON only seems like the pit of success to me.

@yishaigalatzer
Contributor

We have discussed this in detail and we want to come up with a design where even if you have Json+XML getting chrome to return json by default is not a thing.

We believe that for public APIs XML is still a thing you want to expose to make your service more accessible.

For that we decided on three prong attack

  1. We will remove XML from the default formatters (both input and output), and keep JSON.
  2. We will move XML to a separate nuget package. - Microsoft.AspNet.Mvc.Xml, and will have an AddXml() extension method, that will add both formatters and any other settings required to make them play well with the system. We are thinking about leaving XML in the template. With change 3, the chrome issue goes away.
  3. We will ignore accept headers with */* in them for the purpose of content negotiation (which will make all browsers go to the first formatter by default). This behavior will be controllable by MvcOptions.
@yishaigalatzer yishaigalatzer added this to the 6.0.0-rc1 milestone Jan 8, 2015
@glennblock

Yes +1!

@drub0y
drub0y commented Jan 9, 2015

Yes, I agree, JSON should be the only configured formatter by default. 👍

@saramgsilva

yes, +1

@iigorr
iigorr commented Jan 9, 2015

👍 Absolutely

@nvivo
nvivo commented Jan 10, 2015

This is awesome.

I really didn't even thought about the solution 3. The best thing is that if someone wants Xml by default in the browser, they can just switch the order of the formatters. Makes perfect sense, great job!

You probably already talked about it but why not move JSON out to its own DLL as well? My bet is that a couple years from now, another guy will propose removing the "obsolete json format" from the default list and things would be already in place. It would make it also harder to have any accidental dependency on json.

@yishaigalatzer
Contributor

@nvivo these are really good thoughts, we debated them and decided to go with the current approach.

For separating JSON to its own package - We do take dependency on json.net in the JSON result, so we have to have the formatter in the MVC package.

As for having JSON as a default formatter, we are carefully balancing how much code goes in the template, and how easy it is to start with a clean slate with concerns for future compatibility.

Adding a a few lines to change the default formatter is reasonable (and we plan to make it easier and cleaner) see #1787 (we still haven't fully closed on the design, but the intent is to simplify). Our main concern was the compatibility between XML and JSON, and the fact that you have XML end point by default even if you didn't intend to. Like you correctly commented option 3 already addresses the default for browsers.

@AjmalVh
AjmalVh commented Jan 12, 2015

+1 for JSON being default. 👍

@glennblock

JSON has been the default since Web API 1.0. The issue is the XML formatter was also added. To have JSON really be defaulted for all browsers, XML
needs to be removed so that if XML and JSON are sent in the accept JSON will always get selected no matter what.

@yishaigalatzer
Contributor

Broken down into two separate issues #1791 and #1792

@yishaigalatzer yishaigalatzer removed this from the 6.0.0-beta3 milestone Jan 12, 2015
@jamescurran

As far as I can tell, IE, by default, uses an "Accept" heading with both "Application/xhtml+xml" and "*/*" with no priority setting, which WebAPI apparently takes as "JSON is as good as anything else, give him JSON".

Chrome, OTOH, uses an Accept of "text/html, application/xhtml+xml, application/xml; q=0.9, image/webp, */*; q=0.8" which WebAPI reads as "He's rather have XML than JSON; give him that".

Hence your result vary depending on whether you use IE or Chrome.

@yishaigalatzer
Contributor

@jamescurran this used to be the case but not anymore. With the current bits, as soon as we see */*` we opt out of accept header based content negotiation and pick the first formatter available. There is still content negotiation going on based on the runtime type of the object, so the first formatter than can handle the type wins.

The jist of this design is that browsers hitting an api are not really opting into content negotiation, it is more of a way to test an API (not the right way, but a very convenient way). So we would like a consistent experience. A client on the other hand will (should) never provide a */*, as a */* is implied anyways.

@guneysus

+1

@Badspeed
Badspeed commented Mar 9, 2016

+1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment