mülkün web API'sında serileştirilmesini önleme


174

Bir dinlenme API oluşturmak için bir MVC 4 web API ve asp.net web formları 4.0 kullanıyorum. Harika çalışıyor:

[HttpGet]
public HttpResponseMessage Me(string hash)
{
    HttpResponseMessage httpResponseMessage;
    List<Something> somethings = ...

    httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK, 
                                 new { result = true, somethings = somethings });

    return httpResponseMessage;
}

Şimdi bazı özelliklerin serileştirilmesini önlemem gerekiyor. Liste üzerinde bazı LINQ kullanabilirsiniz ve sadece ihtiyacım özellikleri almak biliyorum ve genellikle iyi bir yaklaşım, ama mevcut senaryoda somethingnesne çok karmaşık ve ben farklı yöntemlerde farklı bir özellik kümesi gerekir, bu yüzden daha kolay işaretlenir, çalışma zamanında, göz ardı edilecek her özellik.

Bunu yapmanın bir yolu var mı?


Özelliğe ScriptIgnore ekleyebilirsiniz. bu soruyu görüntüle stackoverflow.com/questions/10169648/…
atbebtg

Yanıtlar:


232

ASP.NET Web API Json.Netvarsayılan biçimlendirici olarak kullanıldığından, uygulamanız yalnızca veri biçimi olarak JSON kullanıyorsa, [JsonIgnore]serileştirme özelliğini yok saymak için kullanabilirsiniz :

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

    [JsonIgnore]
    public List<Something> Somethings { get; set; }
}

Ancak, bu şekilde XML biçimini desteklemez. Yani, durumda başvurunuzun zorundadır kullanmak yerine, destek XML formatında fazla (veya sadece destek XML) Json.Netkullanmanız gerektiğini, [DataContract]JSON ve XML hem destekleyen:

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

Daha fazla anlayış için resmi makaleyi okuyabilirsiniz .


Ben jsonignore kullanarak çalışma zamanında öznitelik eklemek ve kaldırmak için bir yol bulmak zorunda kalacak düşünüyorum.
user1330271

ÇALIŞMA GİBİ ÇALIŞTI! TEŞEKKÜRLER :)
Paulo Rodrigues

JsonIgnore özniteliğinin XML yanıtıyla desteklenmemesi neden üzücü?
Mukus

Datacontract harika bir çözümdür. Bana temiz bir REST API veriyor. Aynı zamanda verileri no-sql içine kaydettiğimde, json olarak saklanan nesnelere rağmen yok sayılan özellikler kalıcı olur.
FrankyHollywood

1
@FedorSteeman JsonIgnore'un ad alanı Newtonsoft.Json'dur, JSON.Net-nuget paketine ihtiyaç duyar. DataContract ve DataMember öznitelikleri ise System.Runtime.Serialization-namespace'e (ve eksikse referansa) ihtiyaç duyar
Esko

113

ASP.NET Web API'sindeki Web API belgeleri sayfasına göre JSON ve XML Serialization özelliği [JsonIgnore], Json serileştiricisi veya [IgnoreDataMember]varsayılan XML serileştiricisi için bir mülkte seri hale getirmeyi açıkça önlemek için kullanılır .

Ancak testte, [IgnoreDataMember]XML ve Json istekleri için serileştirmeyi engellediğini fark ettim , bu yüzden birden çok özniteliğe sahip bir özelliği dekore etmek yerine bunu kullanmanızı tavsiye ederim.


2
Bu daha iyi cevap. Bir öznitelik ile XML ve JSON'u kapsar.
Oliver

17
Ne yazık ki [IgnoreDataMember], tembel yüklü EF 6 proxy nesneleriyle (sanal özellikler) çalışmıyor gibi görünüyor. [DataContract]ve [DataMember]yapmak.
Nick

32

Varsayılan olarak her şeyin seri hale getirilmesine izin vermek yerine "kaydolma" yaklaşımını kullanabilirsiniz. Bu senaryoda, yalnızca belirttiğiniz özelliklerin serileştirilmesine izin verilir. Bunu System.Runtime.Serialization ad alanında bulunan DataContractAttributeve ile yaparsınız .DataMemberAttribute

DataContactAttributeSınıfa uygulanır ve DataMemberAttributesiz Serileştirilecek istediğiniz her üyesine uygulanır:

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

Cesaretin bunun daha iyi bir yaklaşım olduğunu söylüyorum, çünkü sizi serileştirme yoluyla neyi yapıp neyin yapmayacağına dair açık kararlar vermeye zorluyor. Ayrıca, başka bir yerde JSON.net ile serileştirdiğiniz için, model sınıflarınızın JSON.net'e bağımlı olmadan kendi başlarına bir projede yaşamalarını sağlar.


2
Devralınan üyeleri gizlemek için .Net Core ile kutudan çıkan tek yaklaşım. Hem XML hem de Json serileştirme için çalışır. Kudos
Piou

Aynı işlevselliğe ihtiyacım var ama özellikleri dahil veya hariç tutulan api yönteminin çağrılmasına bağlı olarak farklı api çağrıları için farklı veriler gereklidir. Herhangi bir öneri
Nithin Chandran

Bu harika çalışıyor, ancak asıl sorunum bu konfigürasyonların EF Core'daki her dbcontext iskelesi ile ortadan kalkması, bunun için bir çözümü var mı? Bu öznitelikler kısmi bir sınıfta olabilir mi veya programlı olarak ayarlanabilir mi?
Naner

20

Bu benim için çalıştı: String dizi türünün AllowList adlı bir ortak özelliği olan özel bir sözleşme çözümleyici oluşturun. Eyleminizde, eylemin geri dönmesi gereken şeylere bağlı olarak bu özelliği değiştirin.

1. özel bir sözleşme çözücü oluşturun:

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. eylemde özel sözleşme çözücü kullanın

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

Bu yaklaşım, sınıf tanımını değiştirmek yerine belirli bir istek için izin vermeme / vermeme izin verdi. Ve XML serileştirmeye ihtiyacınız yoksa, bunu sizin sitenizde kapatmayı unutmayın App_Start\WebApiConfig.csveya istemci json yerine xml isterse, API'nız engellenen özellikleri döndürür.

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

Daha yeni sürümlerde bir şeyler değişmiş olmalı, ancak bunu çalıştıramadım. Çözümleyiciyi değiştirirken 'as' yerine 'new' yaparak işe yarayabilirim. JsonContractResolver türü bir nedenden dolayı uyumlu değil. Yeni yapmakla ilgili sorun, sadece bir yerine herkesin üzerine yazmasıdır.
Kalel Wade

Bu şekilde bir MediaTypeFormatter alan Request.CreateResponse () yöntemini kullanarak bu çalışmayı başardım: var jsonMediaTypeFormatter = new JsonMediaTypeFormatter {SerializerSettings = new JsonSerializerSettings {ContractResolver = new PublicDomainJsonContractResolverOptIn {Allow "{" Bayt "," MimeType "," Genişlik "," Yükseklik "}}}}; Request.CreateResponse (HttpStatusCode.OK, resim, jsonMediaTypeFormatter);
Paul

Bir XML yanıtında engellenen özelliklerin de yok sayılmasını istiyorsak ne olur?
Carlos P

Veri sözleşmesi çözümleyicisi istek başına bir kez atanmadıkça, bu iş parçacığı için güvenli değildir. Bence bu başlangıç ​​sınıfında bir kez atandı.
Sprague

2
Daha da kötüsü, bunu test ettim ve createproperties çağrısı sözleşme çözücü tarafından önbelleğe alındı. Bu cevap en iyi ihtimalle naiftir, en kötü ihtimalle tehlikelidir.
Sprague

19

Size istediğinizi başarmanın 2 yolunu göstereceğim:

İlk yol: Boşsa, o alanın serileştirilmesini atlamak için alanınızı JsonProperty özniteliğiyle süsleyin.

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

    [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
    public List<Something> Somethings { get; set; }
}

İkinci yol: Bazı karmaşık senaryolarla görüşüyorsanız, belirli bir mantığa bağlı olarak o alanın serileştirilmesini atlamak için Web Api kuralını ("ShouldSerialize") kullanabilirsiniz.

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 kullanır ve seri hale getirme yansıması kullanır, böylece (örneğin) ShouldSerializeFieldX () yöntemini algıladığında, FieldX adındaki alan serileştirilmez.


Web api tarafından yapılmaz, web api serileştirme için varsayılan olarak Json.NET kullanır. Bu işlem web api değil Json.NET tarafından yapılır
Hamid Pourjam

1
İkinci çözüm güzeldir, çünkü Domain alan teknolojisini sadece bazı alanları gizlemek için DTO'ları yeniden yazmaya gerek kalmadan agnostik kalmasına izin verir.
Raffaeu

17

Oyuna geç kaldım, ama anonim nesneler hile yapar:

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

11

IgnoreDataMemberMülkü kullanmayı deneyin

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

5

Neredeyse greatbear302'nin cevabı ile aynı, ancak istek başına ContractResolver oluşturuyorum.

1) Özel bir ContractResolver oluşturun

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) Özel sözleşme çözümleyiciyi çalışırken kullanın

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>"),
            }
        }
    });
}

Düzenle:

Beklendiği gibi çalışmadı (istek başına çözümleyici ayır). Anonim nesneler kullanacağım.

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

4

AutoMapper'ı kullanabilir ve .Ignore()eşlemeyi kullanabilir ve ardından eşlenen nesneyi gönderebilirsiniz

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

3

Sadece şunu ekleyerek iyi çalışır: [IgnoreDataMember]

Mülkiyetin üstünde, gibi:

public class UserSettingsModel
{
    public string UserName { get; set; }
    [IgnoreDataMember]
    public DateTime Created { get; set; }
}

Bu ApiController ile çalışır. Kod:

[Route("api/Context/UserSettings")]
    [HttpGet, HttpPost]
    public UserSettingsModel UserSettings()
    {
        return _contextService.GetUserSettings();
    }

Ayrıca, View-Models'ı "Arka uç" modellerinden izole etmek de daha iyi bir çözümdür, böylece bu bildirimi atlayabilirsiniz. Bu durumda kendimi daha iyi buluyorum.
Dannejaha

0

Bazı nedenlerden dolayı [IgnoreDataMember]benim için her zaman işe yaramaz ve bazen StackOverflowException(veya benzeri) olurum . Bunun yerine (veya ek olarak) i ne zaman böyle bir şey arayan bir desen kullanmaya başladıktan POSTiçinde ing Objectsbenim 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);
}

Yani temelde ben geçmek JObjectve bazen nesnelerini ayrıştırırken bazen sonsuz bir döngüye neden olan yerleşik serileştiricinin neden olduğu aviod sorunlarına alındıktan sonra dönüştürmek.

Birisi bunun kötü bir fikir olmasının bir nedenini biliyorsa, lütfen bana bildirin.

Soruna neden olan bir EntityFramework Class özelliği için aşağıdaki kod olduğunu belirtmek gerekebilir (İki sınıf birbirine başvuruyorsa):

[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);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.