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

Posted

技术标签:

【中文标题】从 MVC 4 Web Api 返回匿名类型失败并出现序列化错误【英文标题】:Returning an anonymous type from MVC 4 Web Api fails with a serialization error 【发布时间】:2013-02-04 09:48:11 【问题描述】:

我刚刚开始使用 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;”的响应正文; charset=utf-8'。

内部异常:

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

关于从 API 控制器返回匿名类型,我有什么不明白的地方?

【问题讨论】:

当您请求 json 而不是 xml 时,您是否尝试并检查过它是否有效?不管怎样,看看这个:***.com/questions/10123371/…,你可以使用HttpResponseMessage作为你的 WebApi 方法的返回类型,并将资源添加到它。 “我在浏览器中点击 URL,所以我明白默认情况下它会尝试返回 XML,但即使我请求 JSON,我也会遇到同样的错误。” 你的浏览器是什么?铬? 是的。无论浏览器如何,它都会失败。 这很奇怪,因为我现在正在这样做,和你一样。如果您使用 JsonResult 并返回 JSON - 您在客户端调用中请求的类型是 JSON,而不是 XML? 【参考方案1】:

如果您查看 Fiddler(此处的示例我使用 Firefox)

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

但是,您可以通过添加一个标头从 Fiddler 创建 假请求

Accept: application/json

会有用的

来自link:

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

如何删除XmlFormatter

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

【讨论】:

【参考方案2】:

您也可以使用 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(也可以在 *** 上找到:https://***.com/a/12492552/1138266)。

【讨论】:

【参考方案3】:

我发现 API 不喜欢返回原始列表。相反,我必须创建一个对象,并将对象的值设置为我的列表;请参阅第一个 return 语句。

例子:

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

【讨论】:

【参考方案4】:

我遇到了类似的问题,解决方法是在 Global.asax.cs 文件。

要添加的sn-p如下:

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

【讨论】:

以上是关于从 MVC 4 Web Api 返回匿名类型失败并出现序列化错误的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Web API 从 MVC 4 控制器失败

ASP.NEt MVC 使用 Web API 返回 Razor 视图

在 MVC Web Api 4 Beta 中从 Json 中删除 Null 属性

如何使用 json 从 web.api 获取返回值到 mvc 控制器?

Asp.Net MVC 中 Web API 的默认控制器操作

根据请求从 MVC Web api 返回 xml 或 json