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

时间:2013-02-19 16:14:52

标签: c# asp.net asp.net-mvc asp.net-mvc-4 asp.net-web-api

我刚刚开始使用MVC 4 Web API,我似乎误解了它是如何工作的。

在Web API之前我有一个像这样的简单MVC动作方法:

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

这样可以正常工作。在新的Web API控制器中,我正在尝试做类似的事情。

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

此操作因序列化错误而失败:

  

'ObjectContent`1'类型无法序列化内容类型'application / xml的响应主体;字符集= UTF-8' 。

内部异常:

  

类型'<> f__AnonymousType1`3 [System.String,System.String,System.String]'无法序列化。请考虑使用DataContractAttribute属性对其进行标记,并使用DataMemberAttribute属性标记要序列化的所有成员。如果类型是集合,请考虑使用CollectionDataContractAttribute对其进行标记。有关其他受支持的类型,请参阅Microsoft .NET Framework文档。

我不了解从API控制器返回匿名类型?

4 个答案:

答案 0 :(得分:31)

如果你看一下Fiddler(我在这里使用Firefox的样本)

enter image description here

默认情况下,来自浏览器的请求接受 application / xml,而不是application / json

但是,你可以通过添加一个标题从Fiddler创建虚假请求

Accept: application/json

它会起作用

来自link

  

XML序列化程序不支持匿名类型或JObject实例。如果将这些功能用于JSON数据,则应从管道中删除XML格式化程序,如本文后面所述。

如何删除XmlFormatter

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

答案 1 :(得分:3)

您还可以使用JsonMediaTypeFormatter,因此您不需要JSONObject和相关类。然后,您可以在控制器类中返回动态类型。

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
        };
    }
}

如果您还想支持Jsonp,您可以继承JsonMediaTypeFormatter并创建自己的JsonpMediaTypeFormatter(也可以在stackoverflow上找到:https://stackoverflow.com/a/12492552/1138266)。

答案 2 :(得分:0)

我发现API不喜欢返回原始列表。相反,我必须创建一个对象,并将对象的值设置为我的列表;看到第一个返回声明。

示例:

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 { });
    }
}

答案 3 :(得分:0)

我遇到了类似的问题,解决方法是向其中添加一个代码段 Global.asax.cs个文件。

要添加的代码段如下:

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings
            .ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        GlobalConfiguration.Configuration.Formatters
            .Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);

我还在Application_Start方法的顶部添加了以上代码行,使该方法看起来像这样:

protected void Application_Start()
    {
        GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings
            .ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        GlobalConfiguration.Configuration.Formatters
            .Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);

        AreaRegistration.RegisterAllAreas();
        GlobalConfiguration.Configure(WebApiConfig.Register);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }