简体   繁体   English

从MVC 4 Web Api返回匿名类型失败,并出现序列化错误

[英]Returning an anonymous type from MVC 4 Web Api fails with a serialization error

I'm just getting started with MVC 4 Web API and I seem to be misunderstanding how it works. 我刚刚开始使用MVC 4 Web API,我似乎误解了它是如何工作的。

Before Web API I had a simple MVC action method like this: 在Web API之前,我有一个简单的MVC操作方法,如下所示:

public JsonResult User()
{
    return Json(new
    {
        firstName = "Joe",
        lastName = "Jacobs",
        email = "joe.jacobs@gmail.com"
    });
}

That would work fine. 那会很好。 In the new web API controller I am trying to do something similar. 在新的Web API控制器中,我正在尝试做类似的事情。

public object User()
{
    return new
    {
        firstName = "Joe",
        lastName = "Jacobs",
        email = "joe.jacobs@gmail.com"
    }
}

This fails with a serialization error: 这会因序列化错误而失败:

The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; 'ObjectContent`1'类型无法序列化内容类型'application / xml的响应主体; charset=utf-8'. 字符集= UTF-8' 。

Inner exception: 内部异常:

Type '<>f__AnonymousType1`3[System.String,System.String,System.String]' cannot be serialized. 类型'<> f__AnonymousType1`3 [System.String,System.String,System.String]'无法序列化。 Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. 请考虑使用DataContractAttribute属性对其进行标记,并使用DataMemberAttribute属性标记要序列化的所有成员。 If the type is a collection, consider marking it with the CollectionDataContractAttribute. 如果类型是集合,请考虑使用CollectionDataContractAttribute对其进行标记。 See the Microsoft .NET Framework documentation for other supported types. 有关其他受支持的类型,请参阅Microsoft .NET Framework文档。

What am I not understanding about returning anonymous type from the API controller? 关于从API控制器返回匿名类型我不明白的是什么?

If you look at the Fiddler (sample in here I use Firefox) 如果你看一下Fiddler(我在这里使用Firefox的示例)

在此输入图像描述

By default, request from browser will accepts application/xml, not application/json 默认情况下,来自浏览器的请求将接受 application / xml,而不是application / json

But, you can create fake request from Fiddler by adding one header: 但是,您可以通过添加一个标头来创建来自Fiddler的虚假请求

Accept: application/json

It will work 它会工作

From the link : 链接

The XML serializer does not support anonymous types or JObject instances. XML序列化程序不支持匿名类型或JObject实例。 If you use these features for your JSON data, you should remove the XML formatter from the pipeline, as described later in this article. 如果将这些功能用于JSON数据,则应从管道中删除XML格式化程序,如本文后面所述。

How to remove XmlFormatter : 如何删除XmlFormatter

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

You could also use the JsonMediaTypeFormatter so you do not need the JSONObject and related classes. 您还可以使用JsonMediaTypeFormatter,因此您不需要JSONObject和相关类。 Then you can return a dynamic type in your controller class. 然后,您可以在控制器类中返回动态类型。

public static void Register(HttpConfiguration config)
{
    config.Formatters.Clear();            
    config.Formatters.Add(new JsonMediaTypeFormatter());
    config.MapHttpAttributeRoutes();
}

public class YourController : ApiController
{        
    [HttpGet, Route("getstuff/{stuffId}")]
    public dynamic Get(string stuffId)
    {
        var stuff = Model.Stuff.Get(stuffId);

        return new {
            success= stuff != null,
            stuffId = stuff.Id,
            name = stuff.Name
        };
    }
}

If you also want to support Jsonp you can inherit the JsonMediaTypeFormatter and create you own JsonpMediaTypeFormatter (which also can be found on stackoverflow: https://stackoverflow.com/a/12492552/1138266 ). 如果您还想支持Jsonp,您可以继承JsonMediaTypeFormatter并创建自己的JsonpMediaTypeFormatter(也可以在stackoverflow上找到: https ://stackoverflow.com/a/12492552/1138266)。

I found that the API didn't like returning a raw list. 我发现API不喜欢返回原始列表。 Instead, I had to create an object, and set the value of the object as my list; 相反,我必须创建一个对象,并将对象的值设置为我的列表; see the first return statement. 看到第一个返回声明。

Example: 例:

public IHttpActionResult GetMessages(int messageFeedId, int lastMessageId) {
    List<Message> messageDomainObjects = MessageService.GetMessages(messageFeedId, lastMessageId);

    if (messageDomainObjects.Any())
    {
        var messages = messageDomainObjects.Select(m => new MessageModel(
            m.Id,
            m.Message,
            m.CreatedDate,
            m.IsActive,
            new UserModel(
                m.User.Id,
                m.User.FirstName,
                m.User.LastName
            )
        ));

        return Ok(new { messages = messages });
    }
    else
    {
        return Ok(new { });
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM