Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider only adding the JSON formatter by default #1

Open
ichengzi opened this issue Aug 28, 2018 · 1 comment
Open

Consider only adding the JSON formatter by default #1

ichengzi opened this issue Aug 28, 2018 · 1 comment
Labels
mark 有意思的issue,收藏

Comments

@ichengzi
Copy link
Owner

ichengzi commented Aug 28, 2018


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.

Originally posted by @jamescurran in aspnet/Mvc#1765 (comment)

@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.

很优雅的处理方式,解决了浏览器*/*调试api问题;因为正常请求不会有*/*header, 故检测到有 */*直接去掉negotiation,取默认的formatter

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.

Originally posted by @yishaigalatzer in aspnet/Mvc#1765 (comment)

@ichengzi ichengzi changed the title 查看过的有意义的issue,收藏 有意思的issue,收藏 Feb 13, 2019
@ichengzi ichengzi added the mark 有意思的issue,收藏 label Feb 13, 2019
@ichengzi ichengzi reopened this Feb 13, 2019
@ichengzi ichengzi changed the title 有意思的issue,收藏 https://github.com/aspnet/Mvc/issues/1765 Consider only adding the JSON formatter by default #1765 Feb 13, 2019
@ichengzi ichengzi changed the title https://github.com/aspnet/Mvc/issues/1765 Consider only adding the JSON formatter by default #1765 Consider only adding the JSON formatter by default Feb 13, 2019
@ichengzi
Copy link
Owner Author

对于匿名对象或者array , xml 和json的序列化方式存在较大差异

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.

Originally posted by @nvivo in aspnet/Mvc#1765 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
mark 有意思的issue,收藏
Projects
None yet
Development

No branches or pull requests

1 participant