防止在api中序列化属性

我正在使用mvc 4 web api和asp.net webforms 4.0来构build其他api。 这工作很好:

[HttpGet] public HttpResponseMessage Me(string hash) { HttpResponseMessage httpResponseMessage; List<Something> somethings = ... httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK, new { result = true, somethings = somethings }); return httpResponseMessage; } 

现在我需要阻止一些属性被序列化。 我知道我可以在列表上使用一些linq,只获得我需要的属性,通常这是一个很好的方法,但在目前的情况下, something对象太复杂了,我需要不同的属性在不同的方法,所以在运行时标记每个属性将会更容易被忽略。

有没有办法做到这一点?

ASP.NET Web API使用Json.Net作为默认的格式化程序,所以如果您的应用程序仅仅使用JSON作为数据格式,则可以使用[JsonIgnore]忽略用于序列化的属性:

 public class Foo { public int Id { get; set; } public string Name { get; set; } [JsonIgnore] public List<Something> Somethings { get; set; } } 

但是,这种方式不支持XML格式。 因此,如果您的应用程序需要更多支持XML格式(或仅支持XML),而不是使用Json.Net ,则应该使用同时支持JSON和XML的[DataContract]

 [DataContract] public class Foo { [DataMember] public int Id { get; set; } [DataMember] public string Name { get; set; } //Ignore by default public List<Something> Somethings { get; set; } } 

为了更多的理解,你可以阅读官方文章 。

根据ASP.NET Web API中的Web API文档页面JSON和XML序列化来显式地阻止对属性的序列化,您可以对Json序列化器使用[IgnoreDataMember] ,对默认XML序列化器使用[JsonIgnore]

但是在testing中,我注意到[IgnoreDataMember]阻止了XML和Json请求的序列化,所以我build议使用它而不是装饰具有多个属性的属性。

不要让所有东西都默认序列化,你可以采取“select”的方法。 在这种情况下,只有您指定的属性被允许被序列化。 您可以使用System.Runtime.Serialization命名空间中的DataContractAttributeDataMemberAttribute完成此操作。

DataContactAttribute应用于该类, DataMemberAttribute应用于要序列化的每个成员:

 [DataContract] public class MyClass { [DataMember] public int Id { get; set;} // Serialized [DataMember] public string Name { get; set; } // Serialized public string DontExposeMe { get; set; } // Will not be serialized } 

我敢说,这是一个更好的方法,因为它迫使你做出明确的决定,不pipe是通过序列化还是不通过。 它也允许你的模型类自己生活在一个项目中,而不需要依赖于JSON.net,只是因为你正在用JSON.net序列化它们。

这对我有效:创build一个自定义合约parsing器,它具有一个名为AllowList的string数组types的公共属性。 在您的操作中,根据操作需要返回的内容修改该属性。

1.创build一个自定义合约parsing器:

 public class PublicDomainJsonContractResolverOptIn : DefaultContractResolver { public string[] AllowList { get; set; } protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) { IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization); properties = properties.Where(p => AllowList.Contains(p.PropertyName)).ToList(); return properties; } } 

2.使用自定义合约parsing器

 [HttpGet] public BinaryImage Single(int key) { //limit properties that are sent on wire for this request specifically var contractResolver = Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver as PublicDomainJsonContractResolverOptIn; if (contractResolver != null) contractResolver.AllowList = new string[] { "Id", "Bytes", "MimeType", "Width", "Height" }; BinaryImage image = new BinaryImage { Id = 1 }; //etc. etc. return image; } 

这种方法允许我允许/不允许特定的请求,而不是修改类的定义。 如果你不需要XML序列化,不要忘记在App_Start\WebApiConfig.csclosures它, App_Start\WebApiConfig.cs如果客户端请求xml而不是json,你的API将返回阻塞的属性。

 //remove xml serialization var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml"); config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType); 

我迟到了,但是一个匿名对象可以做到这一点:

 [HttpGet] public HttpResponseMessage Me(string hash) { HttpResponseMessage httpResponseMessage; List<Something> somethings = ... var returnObjects = somethings.Select(x => new { Id = x.Id, OtherField = x.OtherField }); httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK, new { result = true, somethings = returnObjects }); return httpResponseMessage; } 

我会告诉你两种方法来完成你想要的东西:

第一种方法:装饰你的领域与JsonProperty属性为了跳过该字段的序列化,如果它为空。

 public class Foo { public int Id { get; set; } public string Name { get; set; } [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public List<Something> Somethings { get; set; } } 

第二种方法:如果您正在与一些复杂场景进行协商,那么您可以使用Web Api约定(“ShouldSerialize”)来跳过依赖于某些特定逻辑的该字段的序列化。

 public class Foo { public int Id { get; set; } public string Name { get; set; } public List<Something> Somethings { get; set; } public bool ShouldSerializeSomethings() { var resultOfSomeLogic = false; return resultOfSomeLogic; } } 

WebApi使用JSON.Net,并使用reflection序列化,所以当它检测到(例如)ShouldSerializeFieldX()方法时,名称为FieldX的字段将不会被序列化。

尝试使用IgnoreDataMember属性

 public class Foo { [IgnoreDataMember] public int Id { get; set; } public string Name { get; set; } } 

几乎与greatbear302的答案相同,但是我为每个请求创buildContractResolver。

1)创build一个自定义ContractResolver

 public class MyJsonContractResolver : DefaultContractResolver { public List<Tuple<string, string>> ExcludeProperties { get; set; } protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { JsonProperty property = base.CreateProperty(member, memberSerialization); if (ExcludeProperties?.FirstOrDefault( s => s.Item2 == member.Name && s.Item1 == member.DeclaringType.Name) != null) { property.ShouldSerialize = instance => { return false; }; } return property; } } 

2)使用自定义合约parsing器

 public async Task<IActionResult> Sites() { var items = await db.Sites.GetManyAsync(); return Json(items.ToList(), new JsonSerializerSettings { ContractResolver = new MyJsonContractResolver() { ExcludeProperties = new List<Tuple<string, string>> { Tuple.Create("Site", "Name"), Tuple.Create("<TypeName>", "<MemberName>"), } } }); } 

编辑:

它没有像预期的那样工作(每个请求使用独立的parsing器)。 我将使用匿名对象。

 public async Task<IActionResult> Sites() { var items = await db.Sites.GetManyAsync(); return Json(items.Select(s => new { s.ID, s.DisplayName, s.Url, UrlAlias = s.Url, NestedItems = s.NestedItems.Select(ni => new { ni.Name, ni.OrdeIndex, ni.Enabled, }), })); } 

您可能可以使用AutoMapper并使用.Ignore()映射,然后发送映射的对象

 CreateMap<Foo, Foo>().ForMember(x => x.Bar, opt => opt.Ignore()); 

出于某种原因[IgnoreDataMember]并不总是为我工作,我有时得到StackOverflowException (或类似)。 所以相反(或者另外)我已经开始使用一个模式看起来像这样的时候在Objects POST我的API:

 [Route("api/myroute")] [AcceptVerbs("POST")] public IHttpActionResult PostMyObject(JObject myObject) { MyObject myObjectConverted = myObject.ToObject<MyObject>(); //Do some stuff with the object return Ok(myObjectConverted); } 

所以基本上我传入一个JObject并将其转换后,将其转换为由内置序列化器引起的JObject问题,有时在parsing对象时会导致无限循环。

如果有人知道这是一个坏主意的原因,请让我知道。

可能值得注意的是,下面的代码是导致问题的EntityFramework类属性(如果两个类相互引用):

 [Serializable] public partial class MyObject { [IgnoreDataMember] public MyOtherObject MyOtherObject => MyOtherObject.GetById(MyOtherObjectId); } [Serializable] public partial class MyOtherObject { [IgnoreDataMember] public List<MyObject> MyObjects => MyObject.GetByMyOtherObjectId(Id); }