无法使用Json序列化Web API中的响应


109

我正在使用ASP.NET MVC 5 Web Api。我想咨询所有用户。

我写了api/users,我收到了:

“'ObjectContent`1'类型未能序列化内容类型'application / json; charset = utf-8'的响应主体”

在WebApiConfig中,我已经添加了以下几行:

HttpConfiguration config = new HttpConfiguration();
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; 

但这仍然行不通。

我的返回数据函数是这样的:

public IEnumerable<User> GetAll()
{
    using (Database db = new Database())
    {
        return db.Users.ToList();
    }
}

您试图传递给消费者的价值对象是什么样的?
mckeejm 2014年

非常感谢!只是-我认为应该读为:using(Database db = new Database()){List <UserModel> listOfUsers = new List <UserModel>(); foreach(db.Users中的var user){UserModel userModel = new UserModel(); userModel.FirstName = user.FirstName; userModel.LastName = user.LastName; listOfUsers.Add(userModel); } IEnumerable <UserModel> users = listOfUsers; 回头客;} .. as结果都返回相同的值。
贾里德·惠丁顿

Answers:


76

当涉及从Web Api(或其他任何Web服务)将数据返回给使用者时,我强烈建议不要回传来自数据库的实体。使用模型,您可以控制数据的外观而不是数据库,这是更加可靠和可维护的。这样,您就不必在WebApiConfig中花太多时间去处理格式化程序了。您可以只创建一个具有子模型作为属性的UserModel,并摆脱返回对象中的引用循环。这使串行器更加快乐。

另外,如果仅在请求中指定“ Accepts”标头,则通常不必删除格式化程序或受支持的媒体类型。玩弄这些东西有时会使事情变得更加混乱。

例:

public class UserModel {
    public string Name {get;set;}
    public string Age {get;set;}
    // Other properties here that do not reference another UserModel class.
}

当您提到模型时,您想说我在做什么?返回作为模型的用户的IEnumerable。
CampDev 2014年

5
您正在返回一个实体。实体是指可以通过唯一ID检索的数据库中的对象。您正在从数据库中返回所有用户实体。我建议您创建一个名为“ UserModel”的新类,并为从数据库中获得的每个User实体创建一个数据模型类的新实例,该实例中填充了您要公开的必要信息。返回与用户实体相对的IEnumerable UserModel对象。确保模型属性不引用UserModel类的实例。那就是让您陷入这个问题的原因。
jensendp

3
ncampuzano是正确的,您不想返回自动生成的实体。如果您使用存储过程进行数据库访问,则分隔将更加清晰。您将需要生成一个C#值对象,并将值从IDataReader映射到该值对象。由于您使用的是EF,因此会为您生成类,但是这些类是特殊的EF类,它们比值对象知道的更多。您应该只将“哑”值对象返回给客户端。
mckeejm 2014年

1
@Donny如果您正在控制器中使用DBContext或从数据库返回实体的存储库,则可以将对象映射到控制器中的模型(例如DTO)...但是我更喜欢使用控制器调用返回模型/ DTO的服务。查看AutoMapper-处理映射的出色工具。
2015年

1
@NH。您可以绝对使用上述的恶作剧,但一切都有它的位置。通过访问数据层提供的“实体”通常应保留在数据层中。任何想要在应用程序的业务层中使用此数据的人通常也将以转换后的形式(域对象)使用“实体”。然后,返回给用户和从用户输入的数据通常也将是另一种形式(模型)。同意在任何地方进行这种转换都是很乏味的,但这就是AutoMapper之类的工具真正派上用场的地方。
jensendp

147

如果您使用的是EF,除了在Global.asax上添加以下代码外

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

不要忘记导入

using System.Data.Entity;

然后,您可以返回自己的EF模型

就那么简单!


即使它可能对EF有所帮助,该解决方案也不是针对EF的,它还可以与其他类型的模型一起使用。在Global.asax中,似乎不需要使用。是用于控制器吗?
Matthieu

16
欢迎对此代码的功能及其含义进行一些解释。
雅各布

1
谢谢我面对类似的问题,这个答案帮助我解决了这个问题。
RK_Aus

这个对我有用。无需使用System.Data.Entity添加;到global.asax。谢谢。
MAF博士

有用。只需在Global.asax上简单添加代码,就可以了,无需使用System.Data.Entity进行导入;
Hemant Ramphul

52

给出正确的答案是一种解决方法,但是如果您可以通过一个配置设置来解决它,那就太过分了。

最好在dbcontext构造函数中使用它

public DbContext() // dbcontext constructor
            : base("name=ConnectionStringNameFromWebConfig")
{
     this.Configuration.LazyLoadingEnabled = false;
     this.Configuration.ProxyCreationEnabled = false;
}

Asp.Net Web API错误:“ ObjectContent`1”类型未能序列化内容类型“ application / xml”的响应主体;charset = utf-8'


如果我们从数据库更新模型,您的代码将被删除。
Bimal Das

1
您可以通过删除.tt文件并具有不相交的上下文轻松地将其分开。每次生成模型时,只需在该位置添加新类。@Brimal:您可以关注youtube.com/watch?v=yex0Z6qwe7A
Alim Ul Karim

1
为避免覆盖,可以从edmx属性禁用延迟加载。它为我工作。
Francisco G

@FranciscoG它可以工作,但是如果我们删除edmx并重新生成,它将丢失。
阿林·卡里姆(Alim Ul Karim)女士

1
@BimalDas试试这个youtube.com/…。它不会删除
Alim Ul Karim博士

37

将此代码添加到global.asax以下位置Application_Start

从更新.Ignore.Serialize。它必须工作。

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

1
非常感谢!,您能否更好地解释为什么我们必须删除Xml Formatter才能使其正常工作?
Jav_1

不需要添加json序列化程序(至少在我的情况下),但是需要删除Xml格式。我猜想 xml序列化程序无法序列化匿名类型,并且通过删除它,结果被序列化为json。如果我的猜测是正确的,则可以通过请求MIME类型“ application / json”从控制器中获取数据。
LosManos

11
public class UserController : ApiController
{

   Database db = new Database();

   // construction
   public UserController()
   {
      // Add the following code
      // problem will be solved
      db.Configuration.ProxyCreationEnabled = false;
   }

   public IEnumerable<User> GetAll()
    {
            return db.Users.ToList();
    }
}

哇,对我有用。但为什么?ProxyCreationEnabled属性的作用是什么?
jacktric

和我一起工作,但是这段代码是什么?我还注意到,所有子类都以null检索!
Waleed A. Elgalil '17

10

我不喜欢此代码:

foreach(var user in db.Users)

另一种选择是,可以执行以下操作,这对我有用:

var listOfUsers = db.Users.Select(r => new UserModel
                         {
                             userModel.FirstName = r.FirstName;
                             userModel.LastName = r.LastName;

                         });

return listOfUsers.ToList();

但是,我最终使用了Lucas Roselli的解决方案。

更新:通过返回匿名对象进行简化:

var listOfUsers = db.Users.Select(r => new 
                         {
                             FirstName = r.FirstName;
                             LastName = r.LastName;
                         });

return listOfUsers.ToList();

10

我使用此代码将其解析为WebApiConfig.cs文件

var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects; 
config.Formatters.Remove(config.Formatters.XmlFormatter);

非常感谢。不知道这对安全有什么作用。
阿伦·普拉萨德

6

还有这种情况会产生相同的错误:

如果返回的是List<dynamic>Web API方法

例:

public HttpResponseMessage Get()
{
    var item = new List<dynamic> { new TestClass { Name = "Ale", Age = 30 } };

    return Request.CreateResponse(HttpStatusCode.OK, item);
}

public class TestClass
{
    public string Name { get; set; }
    public int Age { get; set; }
}

因此,对于这种情况,请在返回类(全部)中使用[KnownTypeAttribute],如下所示:

[KnownTypeAttribute(typeof(TestClass))]
public class TestClass
{
    public string Name { get; set; }
    public int Age { get; set; }
}

这对我有用!


如果您将linqivot与动态列一起使用怎么办?
codegrid

[KnownTypeAttribute(typeof(TestClass)))解决了我的问题
Guillaume Raymond

6

将其添加到您Application_Start()Global.asax文件方法中应该可以解决问题

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

方法2:[不推荐]
如果使用EntityFramework,则可以在DbContext类构造函数中禁用代理。注意:如果您更新模型,此代码将被删除

public class MyDbContext : DbContext
{
  public MyDbContext()
  {
    this.Configuration.ProxyCreationEnabled = false;
  }
}

1
谢谢苏曼 我有同样的问题。我正在测试我的Web API,并一直困扰于此问题。您的解决方案可以解决问题。非常感谢。
TarakPrajapati

4

我个人最喜欢的:只需将以下代码添加到中App_Start/WebApiConfig.cs。默认情况下,这将返回json而不是XML,还可以防止出现错误。无需编辑Global.asax删除XmlFormatter等。

“ ObjectContent`1”类型未能序列化内容类型“ application / xml”的响应主体;字符集= utf-8

config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));

2

使用AutoMapper ...

public IEnumerable<User> GetAll()
    {
        using (Database db = new Database())
        {
            var users = AutoMapper.Mapper.DynamicMap<List<User>>(db.Users);
            return users;
        }
    }

2

使用以下名称空间:

using System.Web.OData;

代替 :

using System.Web.Http.OData;

对我有用


2

添加以下行

this.Configuration.ProxyCreationEnabled = false;

两种使用ProxyCreationEnabled方式false

  1. 将其添加到DBContext构造函数中

    public ProductEntities() : base("name=ProductEntities")
    {
        this.Configuration.ProxyCreationEnabled = false;
    }

要么

  1. Get方法内部添加行

    public IEnumerable<Brand_Details> Get()
    {
        using (ProductEntities obj = new ProductEntities())
        {
            this.Configuration.ProxyCreationEnabled = false;
            return obj.Brand_Details.ToList();
        }
    }

2

对我有用的解决方案:

  1. 使用[DataContract]类和[DataMember]属性,每个属性序列化。这足以获得Json结果(例如,来自fiddler)。

  2. 要获得xml序列化,请编写Global.asax以下代码:

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

  3. 阅读本文,它有助于我理解序列化:https : //www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization

1

要添加到jensendp的答案中:

我会将实体传递给用户创建的模型,并使用该实体中的值来设置新创建的模型中的值。例如:

public class UserInformation {
   public string Name { get; set; }
   public int Age { get; set; }

   public UserInformation(UserEntity user) {
      this.Name = user.name;
      this.Age = user.age;
   }
}

然后将您的退货类型更改为: IEnumerable<UserInformation>


1
还有更优雅的方法为您处理翻译,这样您就不必维护每个属性。.AutoMapper和ValueInjecter是2个著名的属性
Sonic Soul

1

这是我的错误

我基本上添加一行

  • Entity.Configuration.ProxyCreationEnabled = false;

到UsersController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using UserDataAccess;

namespace SBPMS.Controllers
{
    public class UsersController : ApiController
    {


        public IEnumerable<User> Get() {
            using (SBPMSystemEntities entities = new SBPMSystemEntities()) {
                entities.Configuration.ProxyCreationEnabled = false;
                return entities.Users.ToList();
            }
        }
        public User Get(int id) {
            using (SBPMSystemEntities entities = new SBPMSystemEntities()) {
                entities.Configuration.ProxyCreationEnabled = false;
                return entities.Users.FirstOrDefault(e => e.user_ID == id);
            }
        }
    }
}

这是我的输出:


1

使用[Serializable]上课:

例:

[Serializable]
public class UserModel {
    public string Name {get;set;}
    public string Age {get;set;}
}

它为我工作!


1
这样说,几乎可以认为以前回答过这篇文章的每个人都是愚蠢的;)…好吧,我严重怀疑他们是这样,所以这很可能意味着该解决方案在2015年问到问题时根本不适用……我自己对该语法了解不多,但是我感觉它要么是相对较新的,要么可能存在一些缺点,使其在某些用例中无法使用。我觉得您的解决方案可能对将来解决此问题的读者有用,但如果您阐明其局限性,那肯定会有所帮助。
jwatkins

1

您将必须在App_Start文件夹中可用的WebApiConfig.cs中定义Serializer Formatter,例如

添加config.Formatters.Remove(config.Formatters.XmlFormatter); //将为您提供JSON格式的数据

添加config.Formatters.Remove(config.Formatters.JsonFormatter); //这将为您提供XML格式的数据


您应该获得奖牌。
TheKrogrammer

0

我收到此错误的另一种情况是,当数据库查询返回空值,但我的用户/视图模型类型设置为不可为空时。例如,将我的UserModel字段从更改intint?


0

当Response-Type不是公开的时,也会发生这种情况!当我使用Visual Studio生成类型时,我返回了一个内部类。

internal class --> public class

0

尽管以上所有答案都是正确的,但您可能需要检查InnerException> ExceptionMessage

如果它说这样的话:“ ObjectContext实例已被处置,不能再用于需要连接的操作。 ”。由于EF的默认行为,这可能是一个问题。

通过在DbContext构造函数中分配LazyLoadingEnabled = false可以解决问题。

public class MyDbContext : DbContext
{
  public MyDbContext()
  {
    this.Configuration.LazyLoadingEnabled = false;
  }
}

有关EF的EagerLoading和LazyLoading行为的详细阅读,请参阅此MSDN文章


0

就我而言,我有类似的错误消息:

“ ObjectContent`1”类型未能序列化内容类型“ application / xml”的响应主体;charset = utf-8'。

但是,当我深入研究时,问题是:

键入“ name.SomeSubRootType”和数据协定名称为“ SomeSubRootType://schemas.datacontract.org/2004/07/WhatEverService”是不正确的。如果您正在使用DataContractSerializer或将任何不是静态已知的类型添加到已知类型的列表中,请考虑使用DataContractResolver-例如,通过使用KnownTypeAttribute属性或将它们添加到传递给序列化程序的已知类型的列表中。

我通过添加解决的方式KnownType

[KnownType(typeof(SomeSubRootType))]
public partial class SomeRootStructureType

这个答案启发了这个问题

参考:https : //msdn.microsoft.com/zh-cn/library/ms730167(v=vs.100).aspx


0

Visual Studio 2017或2019对此完全没有考虑,因为Visual Studio本身要求输出为json格式,而Visual Studio的默认格式为“ XmlFormat”(config.Formatters.XmlFormatter)

Visual Studio应该自动执行此操作,而不是给开发人员带来太多麻烦。

若要更正此问题,请转到WebApiConfig.cs文件,然后添加

var json = config.Formatters.JsonFormatter; json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects; config.Formatters.Remove(config.Formatters.XmlFormatter);

Register(HttpConfiguration config)方法中的“ config.MapHttpAttributeRoutes(); ”之后。这将使您的项目产生json输出。


0

就我而言,我解决了重新创建数据库的问题。我对模型进行了一些更改,并在Package Manager控制台中启动Update-Database,但出现以下错误:

“ ALTER TABLE语句与FOREIGN KEY约束“ FK_dbo.Activities_dbo.Projects_ProjectId”发生冲突。该冲突发生在数据库“ TrackEmAllContext-20190530144302”的表“ dbo.Projects”的“ Id”列中。”


0

如果发生以下情况:如果向WebApiConfig.csGlobal.asax.cs添加代码对您不起作用:

.ToList();

添加.ToList()函数。

我尝试了所有解决方案,但以下方法为我工作:

var allShops = context.shops.Where(s => s.city_id == id)**.ToList()**;
return allShops;

希望对您有所帮助。


0

就我而言,它是在导航属性之前删除virtual关键字(即参考表)时修复的。所以我改变了

public virtual MembershipType MembershipType { get; set; }

至:

public MembershipType MembershipType { get; set; }

0

只需在global.asax中添加以下几行:

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

进口

using System.Data.Entity;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.