【问题标题】:WebAPI not returning XML even with correct Accepts header即使使用正确的 Accepts 标头,WebAPI 也不会返回 XML
【发布时间】:2012-08-03 22:50:02
【问题描述】:

我正在使用 ASP.NET WebAPI RC,并托管了一个 API 控制器,对此没有任何花哨的东西。一切都适用于 JSON,但我正在测试使用 Accepts 标头请求不同的格式,这就是我遇到问题的地方。

我正在使用 jQuery 发出 AJAX 请求,并设置请求的 'dataType' 参数。这会正确设置适当的 Accept 标头,如下所示。

$.ajax({
    type: method,
    url: url,
    dataType: "xml",
    data: data || null,
    success: function (data) {
        // omitted
    }
});

这是提琴手请求/响应的保存。如您所见,Accept 标头显示 application/xml,但 WebAPI 返回 JSON。我也尝试手动将 Accept 标头设置为“application/xml”(因此它也没有 text/html 内容),但无济于事。

我到底错过了什么? (注意:我在数据中截取一些机密信息,但没有对其进行其他调整)

GET http://localhost/insp**snip**6716 HTTP/1.1
Host: localhost
Connection: keep-alive
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.60 Safari/537.1
Accept: application/xml, text/xml, */*; q=0.01
Referer: http://localhost/inspector/api/test?
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: m=34e2:|2c69:t|47ba:t|4e99:t; .INSPECTOR3COOKIE=08BA683091E2A457B1832E9B*snip*F911D9ED97076


HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Persistent-Auth: true
X-Powered-By: ASP.NET
Date: Fri, 03 Aug 2012 22:27:42 GMT
Content-Length: 1816

{"Id":2416716,"ProjectId":36,"Url":"http://ins *snip, but obviously this is JSON not XML *

我想指出我并没有调整 AppStart 中的任何格式化程序或任何东西,据我所知,默认情况下应该启用 JSON 和 XML 格式化程序。

更新:我想通了——在下面查看我自己的答案

【问题讨论】:

    标签: asp.net asp.net-web-api


    【解决方案1】:

    我想通了!

    我的 AppStart 中有这个,因为我想要 Xml 序列化程序而不是 DataContract 序列化程序:

    GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
    

    但是...显然我的模型有一些东西使 Xml 序列化器认为它无法序列化它。我猜这导致 WebAPI 决定使用 JSON 格式化程序。

    这种看似无害的设置实际上会影响使用的格式化程序,这完全不直观。希望 WebAPI 的人看到这一点 :)

    某种工具可以让您了解内容协商过程的输入和输出,以便您可以调试此类问题。

    【讨论】:

    • 你是对的,这正是似乎正在发生的事情。有一个具有 RunNegotiation 方法的 System.Net.Http.Formatting.DefaultContentNegotiator 类。该方法基本上只是循环遍历所有格式化程序并检查它们是否可以处理请求。因此,如果 XmlFormatter 出于某种原因无法将您的模型序列化到输出流 - 它会被跳过并考虑下一个 Formatter - JSON,它恰好能够序列化您的模型。
    • 设置一个 TraceWriter,你会看到很多关于 conneg 机制如何工作的调试信息。在此处查看详细信息asp.net/web-api/overview/testing-and-debugging/…
    • 如果您的对象图中有一个循环,那么 XmlSerializer 将无法处理它。也不支持 TimeSpan。
    • 您可以编写一个直接在模型上调用内容协商的测试,以确保它正确序列化。此外,在 CodePlex 上的最新 Web API 源中(但不在 RC 位中),有一个设置在这种情况下返回 406,而不是选择另一个序列化程序。
    • 我们还应该在这个答案中补充一点,如果存在 DataContractSerializer 或 XmlSerializer 无法处理的任何类型,WebAPI 将回退到 JsonFormatter。两者都需要 DataContract 或 Serializable 属性。
    【解决方案2】:

    我遇到了同样的问题,但通过向我返回的所有模型添加默认构造函数来解决它。

    XML 序列化程序创建空白模型对象,然后通过属性上的设置器填充它。如果 setter 是受保护的或私有的,那么该属性也不会被序列化

    【讨论】:

    • 我遇到了同样的问题。如果返回类型没有无参数构造函数,web api 无法创建它们,因此它会退回到 JSON 序列化程序。
    • 正如这个答案中正确所说的,没有无参数构造函数的类型不适用于XmlSerializer。请记住,这同样适用于所有Interfaces/Abstract classes
    【解决方案3】:

    这个线程中的当前答案已经说明了很多原因,但总结一下,XmlSerializer 只支持有限数量的类型。

    在寻找“最佳”格式化程序时,如 AASoft 正确描述的那样,DefaultContentNegotiator 会询问每个格式化程序是否可以支持特定类型。然后它将这些格式化程序与请求中的接受标头进行匹配。

    如果根据接受标头没有找到任何匹配项,则它会选择第一个可以序列化类型的匹配项,在本例中为 JSON 格式化程序。但是,您可以将 DefaultContentNegotiator 配置为不返回默认格式,而是返回 406 None Accepted 状态代码。这向客户端表明找不到匹配的表示,而不是发送客户端可能无法使用的数据,而是生成错误响应。

    “内容协商改进”部分下的博客“ASP.NET Web API 更新 – 5 月 14 日”[1] 中描述了设置此选项。

    希望这会有所帮助,

    亨里克

    [1]http://blogs.msdn.com/b/henrikn/archive/2012/05/14/asp-net-web-api-updates-may-14.aspx

    【讨论】:

    • 然后继续添加 GlobalConfiguration.Configuration.Services.Replace(typeof(IContentNegotiator), new DefaultContentNegotiator(true)); 到您的 Application_start。
    【解决方案4】:

    已经提供了答案,但我想我会把我的发现发表出来,以便以后对任何人有所帮助。

    罪魁祸首是 IEnumerable。例如,返回包含 IEnumerable 并且永远不会返回 XML 的类 Album 的对象 - 仅返回 JSON。

    我用过

    GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true; 
    

    在 Global.asax 中也是如此。这实际上是 xml 返回所必需的。仍然没有返回 XML。

    然后我将 IEnumerable 更改为 List,它工作正常。看起来 Web API 中的 XML 格式化程序无法处理返回对象中的 IEnumerable。

    希望这会有所帮助。

    【讨论】:

    • 为我工作,我原本以为我可能有循环引用,但事实证明它只是 IEnumerables,而且我到处都是。感谢分享。
    【解决方案5】:

    作为对此的后续行动。当我们在返回模型中有一个对象列表但列表中的对象没有无参数构造函数时,我们遇到了这个问题。我们的代码如下所示:

    public class ReturnClass
    {
        public int Value { get; set; }
    
        public List<OtherClass> ListOfThings { get; set; }
    }
    
    public class OtherClass 
    {
        public int OtherValue { get; set; }
    
        public OtherClass(OtherObject o) 
        {
           this.OtherValue = o.OtherValue;
        }  
    }
    

    我们只需要为 SubClass 对象添加一个无参数的构造函数。

    public class ReturnClass
    {
        public int Value { get; set; }
    
        public List<OtherClass> ListOfThings { get; set; }
    }
    
    public class OtherClass 
    {
        public int OtherValue { get; set; }
    
        public OtherClass(OtherObject o) 
        {
           this.OtherValue = o.OtherValue;
        }
    
        public OtherClass()
        {
        }
    
    }
    

    【讨论】:

      【解决方案6】:

      对可能要序列化的任何属性使用可为空的 int 时要小心。设置了config.Formatters.XmlFormatter.UseXmlSerializer = true 的可空 int 将导致 Web API 返回 JSON,无论您的接受标头说什么

      【讨论】:

        【解决方案7】:

        我只想再添加一个可能发生这种情况的原因,即具有内部 get 或 set 的属性。当你通过按Ctrl-向类添加属性时由​​VS生成的那种。

        点赞public string Foo {get; internal set;}

        【讨论】:

          猜你喜欢
          • 2021-06-23
          • 2012-01-20
          • 2016-05-28
          • 1970-01-01
          • 2017-05-08
          • 2020-07-24
          • 2021-02-12
          • 1970-01-01
          • 2017-12-13
          相关资源
          最近更新 更多