2013-02-13 47 views
12

मुझे x-www-form-urlencoded डेटा पोस्ट करते समय काम करने के लिए कस्टम मॉडल बाध्यकारी होने में बहुत परेशानी हो रही है। मैंने हर तरह से कोशिश की है जिसके बारे में मैं सोच सकता हूं और वांछित परिणाम उत्पन्न करने के लिए कुछ भी नहीं लगता है। ध्यान दें कि जेएसओएन डेटा पोस्ट करते समय, मेरे जेसन कन्वर्टर्स और बहुत आगे सभी ठीक काम करते हैं। यह तब होता है जब मैं x-www-form-urlencoded के रूप में पोस्ट करता हूं कि सिस्टम यह नहीं समझ सकता कि मेरे मॉडल को कैसे बांधना है।एएसपी.Net वेब एपीआई कस्टम मॉडल x-www-form-urlencoded पोस्ट डेटा के साथ बाध्यकारी - कुछ भी काम नहीं करता

मेरा परीक्षण मामला यह है कि मैं अपने मॉडल के हिस्से के रूप में टाइमज़ोनइन्फो ऑब्जेक्ट को बांधना चाहता हूं।

यहाँ मेरी मॉडल बांधने की मशीन है:

public class TimeZoneModelBinder : SystemizerModelBinder 
{ 
    protected override object BindModel(string attemptedValue, Action<string> addModelError) 
    { 
     try 
     { 
      return TimeZoneInfo.FindSystemTimeZoneById(attemptedValue); 
     } 
     catch(TimeZoneNotFoundException) 
     { 
      addModelError("The value was not a valid time zone ID. See the GetSupportedTimeZones Api call for a list of valid time zone IDs."); 
      return null; 
     } 
    } 
} 

यहाँ आधार वर्ग मैं उपयोग कर रहा हूँ है:

public abstract class SystemizerModelBinder : IModelBinder 
{ 
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) 
    { 
     var name = GetModelName(bindingContext.ModelName); 
     var valueProviderResult = bindingContext.ValueProvider.GetValue(name); 
     if(valueProviderResult == null || string.IsNullOrWhiteSpace(valueProviderResult.AttemptedValue)) 
      return false; 

     var success = true; 
     var value = BindModel(valueProviderResult.AttemptedValue, s => 
     { 
      success = false; 
      bindingContext.ModelState.AddModelError(name, s); 
     }); 
     bindingContext.Model = value; 
     bindingContext.ModelState.SetModelValue(name, new System.Web.Http.ValueProviders.ValueProviderResult(value, valueProviderResult.AttemptedValue, valueProviderResult.Culture)); 
     return success; 
    } 

    private string GetModelName(string name) 
    { 
     var n = name.LastIndexOf(".", StringComparison.Ordinal); 
     return n < 0 || n >= name.Length - 1 ? name : name.Substring(n + 1); 
    } 

    protected abstract object BindModel(string attemptedValue, Action<string> addModelError); 
} 

मैं इसे सरल अतिरिक्त कस्टम मॉडल बाइंडरों बनाने के लिए बनाने के लिए इस तरह की एक आधार वर्ग का इस्तेमाल किया।

यहां मेरा मॉडल बाइंडर प्रदाता है। ध्यान दें कि यह मेरे आईओसी कंटेनर से सही ढंग से आवंटित हो रहा है, इसलिए मैं अपने कोड के उस पहलू को दिखाने के लिए परेशान नहीं होगा।

public class SystemizerModelBinderProvider : ModelBinderProvider 
{ 
    public override IModelBinder GetBinder(HttpConfiguration configuration, Type modelType) 
    { 
     if(modelType == typeof(TimeZoneInfo)) 
      return new TimeZoneModelBinder(); 

     return null; 
    } 
} 

अंत में, यहाँ कार्रवाई विधि और मॉडल वर्ग है:

[DataContract)] 
public class TestModel 
{ 
    [DataMember] 
    public TimeZoneInfo TimeZone { get; set; } 
} 

[HttpPost] 
public HttpResponseMessage Test(TestModel model) 
{ 
    return Request.CreateResponse(HttpStatusCode.OK, model); 
} 

कार्रवाई विधि के लिए, मैं कोशिश की है:

public HttpResponseMessage Test([FromBody] TestModel model) 

यह FormUrlEncodedMediaFormatter, जो उपेक्षा करने लगता है invokes मेरी कस्टम मॉडल बांधने की मशीन पूरी तरह से।

public HttpResponseMessage Test([ModelBinder] TestModel model) 

यह मेरा कस्टम मॉडल बांधने की मशीन में कहता है, अपेक्षा के अनुरूप है, लेकिन फिर यह केवल RouteData और QueryString के लिए और किसी कारण शरीर सामग्री के लिए कुछ भी प्रदान नहीं करता है के लिए ValueProviders प्रदान करता है। नीचे देखें:

Value Providers

मैं भी वर्ग में ही सजा ModelBinder(typeof(SystemizerModelBinderProvider))

साथ जब मैं [ModelBinder] विशेषता का उपयोग क्यों मॉडल केवल बाध्यकारी होती है की कोशिश की है, और क्यों यह केवल पढ़ने की कोशिश करता है मार्ग और क्वेरीस्ट्रिंग मान और शरीर की सामग्री को अनदेखा करते हैं? FromBody क्यों मेरे कस्टम मॉडल बाइंडर प्रदाता को अनदेखा करता है?

मैं एक परिदृश्य कैसे बना सकता हूं जहां मैं पोस्ट x-www-form-urlencoded डेटा प्राप्त कर सकता हूं और कस्टम तर्क का उपयोग करके मॉडल गुणों को सफलतापूर्वक बांध सकता हूं?

+0

बस उत्सुक, आप ReSharper प्रयोग करते हैं? –

उत्तर

26

मैं तुम्हें following blog post जिसमें माइक स्टाल विवरण में बताते हैं पढ़ने की सिफारिश करेंगे कैसे वेब एपीआई में मॉडल बंधन काम करता है:

बाध्यकारी मापदंडों के लिए 2 तकनीक रहे हैं: मॉडल बाध्यकारी और formatters। प्रैक्टिस में, वेबएपीआई क्वेरी स्ट्रिंग और बॉडी से पढ़ने के लिए फॉर्मेटर्स से पढ़ने के लिए मॉडल बाइंडिंग का उपयोग करता है।

यहाँ निर्धारित करने के लिए एक पैरामीटर मॉडल बंधन या एक फ़ॉर्मेटर साथ पढ़ा जाता है कि क्या बुनियादी नियम हैं: पैरामीटर उस पर कोई गुण नहीं है

  1. है, तो निर्णय विशुद्ध रूप से पैरामीटर के पर किया जाता है। नेट प्रकार "सरल प्रकार" मॉडल बाध्यकारी का उपयोग करता है। जटिल प्रकार स्वरूपण का उपयोग करता है। एक "सरल प्रकार" में शामिल हैं: प्राइमेटिव्स, टाइमस्पेन, डेटटाइम, ग्रिड, डेसिमल, स्ट्रिंग, या कुछ स्ट्रिंग से कनवर्ट करने वाले टाइपकॉन्टर के साथ।
  2. आप [FromBody] विशेषता का उपयोग यह निर्दिष्ट करने के लिए कर सकते हैं कि पैरामीटर शरीर से पढ़ा जाना चाहिए।
  3. आप पैरामीटर या पैरामीटर के प्रकार पर [ModelBinder] विशेषता का उपयोग कर सकते हैं यह निर्दिष्ट करने के लिए कि पैरामीटर मॉडल बाध्य होना चाहिए। यह विशेषता आपको मॉडल बाइंडर को कॉन्फ़िगर करने देती है। [FromUri] [ModelBinder] का व्युत्पन्न उदाहरण है जो विशेष रूप से मॉडल बाइंडर को केवल यूआरआई में देखने के लिए कॉन्फ़िगर करता है।
  4. शरीर को केवल एक बार पढ़ा जा सकता है। इसलिए यदि आपके पास हस्ताक्षर में 2 जटिल प्रकार हैं, तो उनमें से कम से कम पर एक [मॉडलबिंडर] विशेषता होनी चाहिए।

इसलिए यदि आपके डेटा के स्रोत अनुरोध शरीर तो आप एक कस्टम MediaTypeFormatter बजाय एक मॉडल बांधने की मशीन बना सकते हैं।

+0

क्या FormUrlEncodedMediaFormatter किसी भी तरह का मॉडल बाध्यकारी नहीं करता है? क्या मुझे इससे विरासत मिलनी चाहिए और कुछ ओवरराइड करना चाहिए? –

+1

'FormUrlEncodedMediaTypeFormatter' अनुरोध के निकाय को पढ़ता है और' अनुप्रयोग/एक्स-www-form-urlencoded' अनुरोध को 'KeyValuePair ' के संग्रह में पार्स करने के लिए एक कस्टम पार्सर का उपयोग करता है जिसका उपयोग तब करने के लिए किया जाता है आदर्श। –

+0

MediaTypeFormatter, '[ModelBinder] ',' [FromBody]' या '[ModelBinder (typeof (x))] 'के साथ आप अपनी क्रिया विधि पर किस विशेषता का उपयोग करते थे? – Mourndark

7

मॉडलबिंडर MediaTypeFormatter की तुलना में अपेक्षाकृत बेहतर लगता है। आपको इसे वैश्विक रूप से पंजीकृत करने की आवश्यकता नहीं है।

मुझे वेब एपीआई में जटिल ऑब्जेक्ट प्रकारों को बांधने के लिए मॉडल बाइंडर का उपयोग करने का एक और विकल्प मिला। मॉडल बाइंडर में, मैं अनुरोध निकाय को स्ट्रिंग के रूप में पढ़ रहा हूं और फिर JSON.NET का उपयोग करके आवश्यक वस्तु प्रकार को deserialize करने के लिए। इसका उपयोग जटिल ऑब्जेक्ट प्रकारों के सरणी को मैप करने के लिए भी किया जा सकता है।

public class PollRequestModelBinder : IModelBinder 
{ 
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) 
    { 
     var body = actionContext.Request.Content.ReadAsStringAsync().Result; 
     var pollRequest = JsonConvert.DeserializeObject<PollRequest>(body); 
     bindingContext.Model = pollRequest; 
     return true; 
    } 
} 

और फिर मैं वेब एपीआई नियंत्रक में यह उपयोग कर रहा हूँ इस प्रकार है::

मैं इस प्रकार एक मॉडल बांधने की मशीन जोड़ा

public async Task<PollResponse> Post(Guid instanceId, [ModelBinder(typeof(PollRequestModelBinder))]PollRequest request) 
    { 
     // api implementation 
    } 
+0

अच्छा जवाब, लेकिन डेटा एनोटेशन के माध्यम से मॉडल सत्यापन के बारे में क्या? – Rasmus

+0

JSON.NET JsonProoperty विशेषता (http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonPropertyAttribute.htm) के साथ कुछ सत्यापन प्रदान करता है। आप आवश्यक, अनुमति देने आदि के लिए "आवश्यक" संपत्ति सेट कर सकते हैं –