संपत्ति को वेब एपीआई में क्रमांकित होने से रोकें


174

मैं एक MVC 4 वेब एपीआई और asp.net वेब रूपों 4.0 का उपयोग कर रहा हूँ एक आराम एपीआई का निर्माण। यह बहुत अच्छा काम कर रहा है:

[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ऑब्जेक्ट बहुत जटिल है, और मुझे अलग-अलग तरीकों से गुणों के एक अलग सेट की आवश्यकता है, इसलिए यह है रनटाइम में, प्रत्येक संपत्ति को नजरअंदाज करना आसान है।

क्या ऐसा करने के लिए कोई रास्ता है?


आप संपत्ति में ScriptIgnore जोड़ सकते हैं। इस प्रश्न को देखें stackoverflow.com/questions/10169648/…
atbebtg

जवाबों:


232

ASP.NET वेब एपीआई का उपयोग 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, आपको [DataContract]JSON और XML दोनों का समर्थन करना चाहिए :

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

अधिक समझ के लिए, आप आधिकारिक लेख पढ़ सकते हैं ।


मुझे लगता है कि मुझे jsonignore का उपयोग करके रन टाइम पर विशेषताओं को जोड़ने और हटाने का एक तरीका खोजना होगा।
user1330271 17

एक जादू की तरह काम किया ! धन्यवाद :)
पाउलो रॉड्रिक्स

यह दुखद क्यों है कि XML प्रतिक्रिया के साथ JsonIgnore विशेषता का समर्थन नहीं किया गया है?
मुकुस

डाटाकंट्रेक्ट एक बेहतरीन उपाय है। यह मुझे एक स्वच्छ REST API देता है। उसी समय जब मैं डेटा को नो-एसक्यूएल में सहेजता हूं, तो अनदेखा किए गए गुणों को ऑब्जेक्ट्स के रूप में जमा होने के बावजूद जारी रखा जाता है।
फ्रैंकीहॉलीवुड

1
@FedorSteeman JsonIgnore का नामस्थान Newtonsoft.Json है, जिसे JSON.Net-nuget पैकेज की जरूरत है। DataContract और DataMember (अगर यह याद आ रही है और संदर्भ) दूसरी ओर की जरूरत System.Runtime.Serialization-नाम स्थान पर -attributes
Esko

113

ASP.NET वेब API में वेब एपीआई प्रलेखन पृष्ठ JSON और XML Serialization के अनुसार स्पष्ट रूप से एक संपत्ति पर क्रमांकन को रोकने के [JsonIgnore]लिए जिसे आप या तो JSON सीरियलाइज़र के लिए उपयोग कर सकते हैं या [IgnoreDataMember]डिफ़ॉल्ट XML धारावाहिक के लिए।

हालांकि परीक्षण में मैंने देखा है कि [IgnoreDataMember]XML और Json दोनों अनुरोधों के लिए क्रमांकन को रोकता है, इसलिए मैं कई विशेषताओं के साथ एक संपत्ति को सजाने के बजाय इसका उपयोग करने की सलाह दूंगा।


2
यह बेहतर उत्तर है। यह एक विशेषता के साथ XML और JSON को कवर करता है।
ओलिवर

17
अफसोस की बात [IgnoreDataMember]है कि आलसी-लोडेड ईएफ 6 प्रॉक्सी ऑब्जेक्ट्स (आभासी गुण) के साथ काम करने के लिए प्रकट नहीं होता है। [DataContract]और [DataMember]हालांकि, करते हैं।
निक

32

सब कुछ डिफ़ॉल्ट रूप से क्रमबद्ध होने देने के बजाय , आप "ऑप्ट-इन" दृष्टिकोण ले सकते हैं। इस परिदृश्य में, केवल आपके द्वारा निर्दिष्ट गुणों को क्रमबद्ध किया जा सकता है। आप इसे DataContractAttributeऔर के साथ करते हैंDataMemberAttribute , System.Runtime.Serialization नामस्थान में पाया जाता है ।

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
}

हिम्मत मैं कहता हूं कि यह एक बेहतर तरीका है क्योंकि यह आपको इस बारे में स्पष्ट निर्णय लेने के लिए मजबूर करता है कि यह क्रमबद्धता के माध्यम से क्या करेगा या नहीं करेगा। यह आपके मॉडल वर्गों को JSON.net पर निर्भरता लिए बिना, केवल स्वयं के द्वारा किसी प्रोजेक्ट में रहने की अनुमति देता है, क्योंकि कहीं न कहीं आप JSON.net के साथ इन्हें प्रसारित करते हैं।


2
एकमात्र दृष्टिकोण जो विरासत में मिले सदस्यों को छिपाने के लिए .Net कोर के साथ बॉक्स से बाहर काम करता है। XML और Json क्रमांकन दोनों के लिए काम करता है। कुदोस
पिउ

मुझे एक ही फ़ंक्शनलिटी की आवश्यकता है लेकिन गुण शामिल हैं या बाहर रखा गया है, इस पर निर्भर करता है कि किस विधि को लागू किया जाता है यानी अलग-अलग एपीआई कॉल के लिए अलग-अलग डेटा की आवश्यकता होती है। कोई सुझाव
नितिन चंद्रन

यह बहुत अच्छा काम करता है, लेकिन मेरा मुख्य मुद्दा यह है कि ये कॉन्फ़िगरेशन ईएफ कोर में हर dbcontext मचान के साथ चले जाते हैं, क्या किसी को इसके लिए वर्कअराउंड है? क्या ये विशेषताएँ आंशिक वर्ग में हो सकती हैं, या प्रोग्रामेटिक रूप से सेट की जा सकती हैं?
नानर

20

इसने मेरे लिए काम किया: एक कस्टम कॉन्ट्रैक्ट रिज़ॉल्वर बनाएं, जिसके पास एक सार्वजनिक संपत्ति है, जिसे AllowList of string array type कहा जाता है। अपनी कार्रवाई में, उस संपत्ति को संशोधित करें, जिसके आधार पर कार्रवाई को वापस करने की आवश्यकता है।

1. एक कस्टम अनुबंध रिज़ॉल्वर बनाएं:

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. कार्रवाई में कस्टम अनुबंध रिज़ॉल्वर का उपयोग करें

[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.csया अपने एपीआई में बंद करने के लिए मत भूलना अगर क्लाइंट jml के बजाय xml का अनुरोध करता है तो आप अवरुद्ध गुणों को वापस कर देंगे।

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

नए संस्करणों के साथ कुछ बदल गया होगा, लेकिन मैं यह काम करने में असमर्थ था। मैं इसे रिवाल्वर को मोडिफाई करते समय 'की तरह' के बजाय 'नया' करके काम कर सकता था। JsonContractResolver प्रकार किसी कारण से प्रशंसनीय नहीं है। नया करने के साथ मुद्दा यह है कि यह केवल एक के बजाय सभी के लिए इसे अधिलेखित कर देता है।
कलल वेड

मैं Request.CreateResponse () विधि का उपयोग करके इस कार्य को प्राप्त करने में कामयाब रहा, जो इस तरह से MediaTypeFormatter प्राप्त करता है: var jsonMediaTypeFormatter = नया JsonMediaTypeFormatter {SerialSettingsing = new JsonSerializerSettings {ContractResolver = new PublicDomainJontortJonContentJson_emontenton.nic.in बाइट्स "," माइम टाइप "," चौड़ाई "," ऊँचाई "}}}}; वापसी का अनुरोध करें। रिट्रीट (HttpStatusCode.OK, छवि, jsonMediaTypeFatteratter);
पॉल

क्या होगा अगर हम एक XML प्रतिक्रिया में अवरुद्ध गुणों को अनदेखा करना चाहते हैं?
कार्लोस पी

जब तक अनुरोध के अनुसार डेटा अनुबंध रिज़ॉल्वर असाइन नहीं किया जाता है, यह थ्रेड सुरक्षित नहीं है। मुझे लगता है कि यह एक बार, स्टार्टअप वर्ग में सौंपा गया है।
Sprague

2
इससे भी बदतर, ive ने यह परीक्षण किया और क्रिप्टोकरंसी कॉल कॉन्ट्रैक्ट रिज़ॉल्वर द्वारा कैश की गई है। यह उत्तर सबसे अच्छा है, सबसे खराब पर खतरनाक है।
Sprague

19

मैं आपको यह दिखाने के लिए 2 तरीके बताऊंगा कि आप क्या चाहते हैं:

पहला तरीका: अपने क्षेत्र को JsonProperty विशेषता के साथ सजाने के लिए यदि यह शून्य है तो उस क्षेत्र के क्रमांकन को छोड़ दें।

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

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

दूसरा तरीका: यदि आप कुछ जटिल परिदृश्यों के साथ बातचीत कर रहे हैं, तो आप कुछ विशिष्ट तर्क के आधार पर उस क्षेत्र के क्रमांकन को छोड़ने के लिए वेब एपी कन्वेंशन ("कंधे से कंधा मिलाकर") का उपयोग कर सकते हैं।

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 का उपयोग करता है और यह परावर्तन के लिए प्रतिबिंब का उपयोग करता है, इसलिए जब यह (उदाहरण के लिए) का पता लगाया गया है तो ShouldSerializeFieldX () फ़ील्ड नाम के साथ फ़ील्ड को क्रमबद्ध नहीं किया जाएगा।


यह वेब एपीआई द्वारा नहीं किया जाता है, वेब एपि सीरीज़िंग के लिए डिफ़ॉल्ट रूप से Json.NET का उपयोग करता है। यह प्रक्रिया Json.NET द्वारा नहीं वेब एप द्वारा की जाती है
हामिद पूरजम

1
दूसरा समाधान अच्छा है क्योंकि यह केवल कुछ क्षेत्रों को छिपाने के लिए डीटीओ को फिर से लिखने की आवश्यकता के बिना डोमेन ऑब्जेक्ट तकनीक को अज्ञेय रखने की अनुमति देता है।
रफ्फू

17

मुझे खेल में देर हो गई है, लेकिन एक अनाम वस्तु चाल कर देगी:

[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

IgnoreDataMemberसंपत्ति का उपयोग करके देखें

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

5

लगभग greatbear302 के जवाब के रूप में एक ही है, लेकिन मैं अनुरोध प्रति अनुबंधResolver बना।

1) एक कस्टम अनुबंध बनाएँ

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) कार्रवाई में कस्टम अनुबंध रिज़ॉल्वर का उपयोग करें

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

संपादित करें:

यह अपेक्षा के अनुरूप काम नहीं किया (प्रति अनुरोध के अनुसार अलग-थलग)। अनाम वस्तुओं का उपयोग करूँगा।

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 का उपयोग करने .Ignore()और मैपिंग का उपयोग करने में सक्षम हो सकते हैं और फिर मैप किए गए ऑब्जेक्ट को भेज सकते हैं

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

3

बस जोड़कर ठीक काम करता है: [इग्नोरडैम्बर]

प्रॉम्प्ट के ऊपर, जैसे:

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

यह ApiController के साथ काम करता है। कोड:

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

इसके अलावा शायद एक बेहतर समाधान "बैक एंड" मॉडल से व्यू-मॉडल को अलग करना है, ताकि आप इस घोषणा को छोड़ सकें। मैं अक्सर खुद को उस स्थिति में बेहतर पाता हूं।
Dannejaha

0

किसी कारण के लिए [IgnoreDataMember] हमेशा मेरे लिए काम नहीं करता है, और मैं कभी-कभी StackOverflowException(या समान) मिलता हूं । तो इसके बजाय (या इसके अलावा) मैं एक पैटर्न का उपयोग करना शुरू कर दिया है जब मेरे एपीआई POSTमें आईएनजी कुछ इस तरह दिख रहा Objectsहै:

[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 और इसे तब परिवर्तित करता जब इसे अंतर्निहित धारावाहिक से उत्पन्न होने वाली समस्याओं से छुटकारा दिलाया जाता है जो कभी-कभी वस्तुओं को पार्स करते समय एक अनंत लूप का कारण बनते हैं।

अगर किसी को कोई कारण पता है कि यह किसी भी तरह से एक बुरा विचार है, तो कृपया मुझे बताएं।

यह ध्यान देने योग्य हो सकता है कि यह एंटिटीफ्रेमवर्क क्लास-प्रॉपर्टी के लिए निम्न कोड है जो समस्या का कारण बनता है (यदि दो वर्ग एक-दूसरे को संदर्भित करते हैं):

[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);
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.