ASP.NET वेब API में पूर्णांकों की एक सरणी पास करें?


427

मेरे पास ASP.NET वेब API (संस्करण 4) REST सेवा है जहाँ मुझे पूर्णांक की एक सरणी को पास करने की आवश्यकता है।

यहाँ मेरी क्रिया विधि है:

public IEnumerable<Category> GetCategories(int[] categoryIds){
// code to retrieve categories from database
}

और यह वह URL है जिसे मैंने आज़माया है:

/Categories?categoryids=1,2,3,4

1
मुझे "/ श्रेणियाँ? श्रेणी = 1 & श्रेणी = 2 और श्रेणी = 3" जैसे क्वेरिस्ट्रिंग का उपयोग करते हुए "अनुरोध की सामग्री के लिए कई मापदंडों को बाँध नहीं सकता" मिल रहा था। आशा है कि यह उन लोगों को यहां लाता है जो इसे एक ही त्रुटि प्राप्त कर रहे थे।
जोश नू

1
@ जोश [क्या आपने हालांकि [फ्रायरी] का उपयोग किया है? सार्वजनिक IEnumerable <श्रेणी> GetCategories ([FromUri] int [] वर्ग) {...}
अनूप कटेल

2
@FrankGorman नहीं, मैं नहीं था, जो मेरा मुद्दा था।
जोश नूर

जवाबों:


619

आपको बस [FromUri]पैरामीटर से पहले जोड़ना होगा , जैसा दिखता है:

GetCategories([FromUri] int[] categoryIds)

और अनुरोध भेजें:

/Categories?categoryids=1&categoryids=2&categoryids=3 

18
क्या होगा अगर मुझे नहीं पता कि मेरे पास सरणी में कितने चर हैं? यदि यह 1000 जैसा है तो क्या होगा? अनुरोध ऐसा नहीं होना चाहिए।
सहार चौ।

7
इससे मुझे एक त्रुटि मिलती है "उसी कुंजी के साथ एक आइटम पहले ही जोड़ा जा चुका है।" हालांकि यह कैडेट्स को स्वीकार करता है [0] = 1 और श्रेणी [1] = 2 और आदि ...
डॉक्टर्स जोन्स

19
यह स्वीकृत उत्तर होना चाहिए - @ हेमांशु भोजक: क्या आपकी पिक लेने का समय नहीं है?
डेविड रीटेनबैकर

12
इसका कारण ASP.NET वेब API वेबसाइट के निम्नलिखित कथन के कारण है जो पैरामीटर बाइंडिंग के बारे में बात कर रहा है: "यदि पैरामीटर" सरल "प्रकार है, तो वेब API URI से मान प्राप्त करने का प्रयास करता है। सरल प्रकारों में शामिल हैं। नेट आदिम प्रकार (इंट, बूल, डबल, और इसके बाद), प्लस टाइमस्पैन, डेटाइम, गाइड, दशमलव और स्ट्रिंग, साथ ही टाइप कन्वर्टर के साथ कोई भी प्रकार जो स्ट्रिंग से परिवर्तित हो सकता है। " a int [] एक साधारण प्रकार नहीं है।
Tr1stan

3
यह मेरे लिए अच्छा काम करता है। एक बिंदु। सर्वर कोड पर, सरणी पैरामीटर को काम करने के लिए पहले आना पड़ता है और किसी भी अन्य पैरामीटर के बाद। अनुरोध में मापदंडों में खिलाते समय, आदेश महत्वहीन है।
छिड़

102

जैसा कि फिलिप डब्ल्यू बताता है, आपको इस तरह के एक कस्टम मॉडल बाइंडर का सहारा लेना पड़ सकता है (वास्तविक प्रकार के परम के लिए बाध्य करने के लिए संशोधित):

public IEnumerable<Category> GetCategories([ModelBinder(typeof(CommaDelimitedArrayModelBinder))]long[] categoryIds) 
{
    // do your thing
}

public class CommaDelimitedArrayModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        var key = bindingContext.ModelName;
        var val = bindingContext.ValueProvider.GetValue(key);
        if (val != null)
        {
            var s = val.AttemptedValue;
            if (s != null)
            {
                var elementType = bindingContext.ModelType.GetElementType();
                var converter = TypeDescriptor.GetConverter(elementType);
                var values = Array.ConvertAll(s.Split(new[] { ","},StringSplitOptions.RemoveEmptyEntries),
                    x => { return converter.ConvertFromString(x != null ? x.Trim() : x); });

                var typedValues = Array.CreateInstance(elementType, values.Length);

                values.CopyTo(typedValues, 0);

                bindingContext.Model = typedValues;
            }
            else
            {
                // change this line to null if you prefer nulls to empty arrays 
                bindingContext.Model = Array.CreateInstance(bindingContext.ModelType.GetElementType(), 0);
            }
            return true;
        }
        return false;
    }
}

और फिर आप कह सकते हैं:

/Categories?categoryids=1,2,3,4और ASP.NET वेब एपीआई आपके categoryIdsसरणी को सही ढंग से बांध देगा ।


10
यह SRP और / या SoC का उल्लंघन कर सकता है, लेकिन आप आसानी से इसे भी इनहेरिट कर सकते हैं, ModelBinderAttributeइसलिए इसका उपयोग सीधे typeof()तर्क का उपयोग करके श्रमसाध्य वाक्यविन्यास के बजाय किया जा सकता है। आपको बस इतना करना है तो तरह के वारिस है: CommaDelimitedArrayModelBinder : ModelBinderAttribute, IModelBinderऔर उसके बाद एक डिफ़ॉल्ट निर्माता है कि आधार वर्ग के लिए नीचे प्रकार परिभाषा धक्का प्रदान करते हैं: public CommaDelimitedArrayModelBinder() : base(typeof(CommaDelimitedArrayModelBinder)) { }
स्लाइडरहॉसरल्स

अन्यथा, मैं वास्तव में इस समाधान को पसंद करता हूं और अपने प्रोजेक्ट में इसका उपयोग कर रहा हूं, इसलिए ... धन्यवाद। :)
स्लाइडरहॉसरल्स

आ एक तरफ ध्यान दें, यह समाधान जेनरिक के साथ की तरह काम नहीं करता है System.Collections.Generic.List<long>के रूप में bindingContext.ModelType.GetElementType()केवल समर्थन System.Arrayप्रकार
ViRuSTriNiTy

@ViRuSTriNiTy: यह सवाल और जवाब विशेष रूप से Arrays के बारे में बात करते हैं। यदि आपको एक सामान्य सूची आधारित समाधान की आवश्यकता है, तो इसे लागू करने के लिए काफी तुच्छ है। एक अलग सवाल उठाने के लिए स्वतंत्र महसूस करें यदि आप सुनिश्चित नहीं हैं कि इसके बारे में कैसे जाना जाए।
मर्चिफ़

2
@codeMonkey: सरणी को शरीर में डालने से POST अनुरोध के लिए अच्छा अर्थ होता है, लेकिन GET अनुरोधों के बारे में क्या? इनमें आमतौर पर शरीर की कोई सामग्री नहीं होती है।
stakx - अब

40

मैं हाल ही में खुद इस आवश्यकता के पार आया था, और मैंने ActionFilterइसे संभालने के लिए एक को लागू करने का निर्णय लिया ।

public class ArrayInputAttribute : ActionFilterAttribute
{
    private readonly string _parameterName;

    public ArrayInputAttribute(string parameterName)
    {
        _parameterName = parameterName;
        Separator = ',';
    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (actionContext.ActionArguments.ContainsKey(_parameterName))
        {
            string parameters = string.Empty;
            if (actionContext.ControllerContext.RouteData.Values.ContainsKey(_parameterName))
                parameters = (string) actionContext.ControllerContext.RouteData.Values[_parameterName];
            else if (actionContext.ControllerContext.Request.RequestUri.ParseQueryString()[_parameterName] != null)
                parameters = actionContext.ControllerContext.Request.RequestUri.ParseQueryString()[_parameterName];

            actionContext.ActionArguments[_parameterName] = parameters.Split(Separator).Select(int.Parse).ToArray();
        }
    }

    public char Separator { get; set; }
}

मैं इसे इस तरह से लागू कर रहा हूं (ध्यान दें कि मैंने 'आईडी' का उपयोग किया था, 'आईडी' का नहीं, क्योंकि यह मेरे मार्ग में निर्दिष्ट है:

[ArrayInput("id", Separator = ';')]
public IEnumerable<Measure> Get(int[] id)
{
    return id.Select(i => GetData(i));
}

और सार्वजनिक url होगा:

/api/Data/1;2;3;4

अपनी विशिष्ट जरूरतों को पूरा करने के लिए आपको इसे रिफ्लेक्टर करना पड़ सकता है।


1
आपके समाधान में प्रकार int हार्डकोड (int.Parse) हैं। इम्हो, @ मृचफ का समाधान बेहतर है
रोजन

27

मामले में किसी की आवश्यकता होगी - (नष्ट) की तरह के माध्यम से एक या समान बात को प्राप्त करने POSTके बजाय FromUri, उपयोग FromBodyऔर क्लाइंट साइड (जे एस / jQuery) स्वरूप परम के रूप में पर$.param({ '': categoryids }, true)

सी#:

public IHttpActionResult Remove([FromBody] int[] categoryIds)

jQuery:

$.ajax({
        type: 'POST',
        data: $.param({ '': categoryids }, true),
        url: url,
//...
});

इसके साथ बात $.param({ '': categoryids }, true)यह है कि यह .net पोस्ट बॉडी से =1&=2&=3बिना पैरामीटर नाम और जैसे कोष्ठक के बिना urlencoded मान सम्‍मिलित करने की अपेक्षा करेगा ।


2
किसी POST का सहारा लेने की आवश्यकता नहीं है। @Lavel उत्तर देखें।
एंड्रे वेर्लंग

3
URI में आप कितना डेटा भेज सकते हैं इसकी एक सीमा है। और मानक के अनुसार, यह एक GET अनुरोध नहीं होना चाहिए क्योंकि यह वास्तव में डेटा को संशोधित कर रहा है।
वर्थ 7

1
और वास्तव में आपने यहाँ एक GET कहाँ देखा है? :)
सोफीजा

3
@ सोफीजा ओपी कहते हैं code to retrieve categories from database, इस प्रकार विधि एक GET विधि होनी चाहिए, POST नहीं।
अज़ीमुथ

22

वेब एपीआई के लिए सरणी पारम भेजने का आसान तरीका

एपीआई

public IEnumerable<Category> GetCategories([FromUri]int[] categoryIds){
 // code to retrieve categories from database
}

Jquery: अनुरोध के रूप में JSON वस्तु भेजें

$.get('api/categories/GetCategories',{categoryIds:[1,2,3,4]}).done(function(response){
console.log(response);
//success response
});

यह आपके अनुरोध URL को उत्पन्न करेगा ../api/categories/GetCategories?categoryIds=1&categoryIds=2&categoryIds=3&categoryIds=4


3
यह स्वीकृत उत्तर से अलग कैसे है? jquery के माध्यम से अजाक्स अनुरोध को लागू करने के अपवाद के साथ जिसका मूल पद से कोई लेना-देना नहीं था।
sksallaj

13

वेबपे से JSON वापस पाने के लिए आप इस कोड को कॉमा से अलग किए गए मान / मानों के लिए ले सकते हैं

 public class CategoryController : ApiController
 {
     public List<Category> Get(String categoryIDs)
     {
         List<Category> categoryRepo = new List<Category>();

         String[] idRepo = categoryIDs.Split(',');

         foreach (var id in idRepo)
         {
             categoryRepo.Add(new Category()
             {
                 CategoryID = id,
                 CategoryName = String.Format("Category_{0}", id)
             });
         }
         return categoryRepo;
     }
 }

 public class Category
 {
     public String CategoryID { get; set; }
     public String CategoryName { get; set; }
 } 

आउटपुट:

[
{"CategoryID":"4","CategoryName":"Category_4"}, 
{"CategoryID":"5","CategoryName":"Category_5"}, 
{"CategoryID":"3","CategoryName":"Category_3"} 
]

12

ASP.NET Core 2.0 सॉल्यूशन (स्वैगर रेडी)

इनपुट

DELETE /api/items/1,2
DELETE /api/items/1

कोड

प्रदाता लिखें (MVC कैसे उपयोग करने के लिए बांधने वाला जानता है)

public class CustomBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (context.Metadata.ModelType == typeof(int[]) || context.Metadata.ModelType == typeof(List<int>))
        {
            return new BinderTypeModelBinder(typeof(CommaDelimitedArrayParameterBinder));
        }

        return null;
    }
}

वास्तविक बाइंडर लिखें (अनुरोध, कार्रवाई, मॉडल, प्रकार, जो भी हो) के बारे में सभी प्रकार की जानकारी प्राप्त करें

public class CommaDelimitedArrayParameterBinder : IModelBinder
{

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {

        var value = bindingContext.ActionContext.RouteData.Values[bindingContext.FieldName] as string;

        // Check if the argument value is null or empty
        if (string.IsNullOrEmpty(value))
        {
            return Task.CompletedTask;
        }

        var ints = value?.Split(',').Select(int.Parse).ToArray();

        bindingContext.Result = ModelBindingResult.Success(ints);

        if(bindingContext.ModelType == typeof(List<int>))
        {
            bindingContext.Result = ModelBindingResult.Success(ints.ToList());
        }

        return Task.CompletedTask;
    }
}

इसे MVC के साथ पंजीकृत करें

services.AddMvc(options =>
{
    // add custom binder to beginning of collection
    options.ModelBinderProviders.Insert(0, new CustomBinderProvider());
});

स्वैगर के लिए एक अच्छी तरह से प्रलेखित नियंत्रक के साथ नमूना उपयोग

/// <summary>
/// Deletes a list of items.
/// </summary>
/// <param name="itemIds">The list of unique identifiers for the  items.</param>
/// <returns>The deleted item.</returns>
/// <response code="201">The item was successfully deleted.</response>
/// <response code="400">The item is invalid.</response>
[HttpDelete("{itemIds}", Name = ItemControllerRoute.DeleteItems)]
[ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(void), StatusCodes.Status404NotFound)]
public async Task Delete(List<int> itemIds)
=> await _itemAppService.RemoveRangeAsync(itemIds);

EDIT: Microsoft इस दृष्टिकोण पर ऑपरेशन के इन बच्चों के लिए एक टाइपकोर्टर का उपयोग करने की सिफारिश करता है । तो नीचे दिए गए पोस्टर सलाह का पालन करें और अपने कस्टम प्रकार को एक स्कीमाफिल्टर के साथ दस्तावेज़ित करें।


मुझे लगता है कि एमएस पुनर्संयोजन जिसके बारे में आप बात कर रहे हैं, वह इस जवाब से संतुष्ट है: stackoverflow.com/a/49563970/4367683
मचाडो

क्या तुमने यह देखा? github.com/aspnet/Mvc/pull/7967 ऐसा लगता है मानो उन्होंने एक विशेष बाइंडर की आवश्यकता के बिना क्वेरी स्ट्रिंग में सूची <जो कुछ भी> शुरू करने के लिए एक फिक्स जोड़ा है। साथ ही आपके द्वारा लिंक किया गया पोस्ट ASPNET कोर नहीं है और मुझे नहीं लगता कि मेरी स्थिति से मदद मिलती है।
विक्टोरियो बेर्रा

सबसे अच्छा, गैर-हैकरी जवाब।
एरिक फिलिप्स

7

मैंने मूल रूप से उस समाधान का उपयोग किया है जो @Mrchief वर्षों के लिए है (यह बहुत अच्छा काम करता है)। लेकिन जब मैंने एपीआई प्रोजेक्ट के लिए अपनी परियोजना में स्वैगर को जोड़ा तो मेरा अंतिम बिंदु दिखाई नहीं दे रहा था।

यह मुझे थोड़ी देर लगी, लेकिन यह वही है जो मैं लेकर आया हूं। यह स्वैगर के साथ काम करता है, और आपके एपीआई विधि हस्ताक्षर साफ दिखते हैं:

अंत में आप कर सकते हैं:

    // GET: /api/values/1,2,3,4 

    [Route("api/values/{ids}")]
    public IHttpActionResult GetIds(int[] ids)
    {
        return Ok(ids);
    }

WebApiConfig.cs

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Allow WebApi to Use a Custom Parameter Binding
        config.ParameterBindingRules.Add(descriptor => descriptor.ParameterType == typeof(int[]) && descriptor.ActionDescriptor.SupportedHttpMethods.Contains(HttpMethod.Get)
                                                           ? new CommaDelimitedArrayParameterBinder(descriptor)
                                                           : null);

        // Allow ApiExplorer to understand this type (Swagger uses ApiExplorer under the hood)
        TypeDescriptor.AddAttributes(typeof(int[]), new TypeConverterAttribute(typeof(StringToIntArrayConverter)));

        // Any existing Code ..

    }
}

एक नया वर्ग बनाएँ: CommaDelimitedArrayParameterBinder.cs

public class CommaDelimitedArrayParameterBinder : HttpParameterBinding, IValueProviderParameterBinding
{
    public CommaDelimitedArrayParameterBinder(HttpParameterDescriptor desc)
        : base(desc)
    {
    }

    /// <summary>
    /// Handles Binding (Converts a comma delimited string into an array of integers)
    /// </summary>
    public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
                                             HttpActionContext actionContext,
                                             CancellationToken cancellationToken)
    {
        var queryString = actionContext.ControllerContext.RouteData.Values[Descriptor.ParameterName] as string;

        var ints = queryString?.Split(',').Select(int.Parse).ToArray();

        SetValue(actionContext, ints);

        return Task.CompletedTask;
    }

    public IEnumerable<ValueProviderFactory> ValueProviderFactories { get; } = new[] { new QueryStringValueProviderFactory() };
}

एक नया वर्ग बनाएँ: StringToIntArrayConverter.cs

public class StringToIntArrayConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
    }
}

टिप्पणियाँ:

  • https://stackoverflow.com/a/47123965/862011 ने मुझे सही दिशा में इशारा किया
  • स्वैगर [मार्ग] विशेषता का उपयोग करते समय केवल मेरे अल्पविराम सीमांकित बिंदुओं को लेने में विफल रहा था

1
मामले में किसी और को यह उपयोग करता है पुस्तकालयों पर जानकारी की जरूरत है। यहाँ "CommaDelimitedArrayParameterBinder" का उपयोग किया जा रहा है। System.Collections.Generic का उपयोग कर; System.Linq का उपयोग करना; System.Threading का उपयोग करना; System.Threading.Tasks का उपयोग कर; System.Web.Http.Controllers का उपयोग कर; System.Web.Http.Metadata का उपयोग कर; System.Web.Http.ModelBinding का उपयोग करना; System.Web.Http.ValueProviders का उपयोग करना; System.Web.Http.ValueProviders.Providers का उपयोग कर;
स्टेकडेवी

6
public class ArrayInputAttribute : ActionFilterAttribute
{
    private readonly string[] _ParameterNames;
    /// <summary>
    /// 
    /// </summary>
    public string Separator { get; set; }
    /// <summary>
    /// cons
    /// </summary>
    /// <param name="parameterName"></param>
    public ArrayInputAttribute(params string[] parameterName)
    {
        _ParameterNames = parameterName;
        Separator = ",";
    }

    /// <summary>
    /// 
    /// </summary>
    public void ProcessArrayInput(HttpActionContext actionContext, string parameterName)
    {
        if (actionContext.ActionArguments.ContainsKey(parameterName))
        {
            var parameterDescriptor = actionContext.ActionDescriptor.GetParameters().FirstOrDefault(p => p.ParameterName == parameterName);
            if (parameterDescriptor != null && parameterDescriptor.ParameterType.IsArray)
            {
                var type = parameterDescriptor.ParameterType.GetElementType();
                var parameters = String.Empty;
                if (actionContext.ControllerContext.RouteData.Values.ContainsKey(parameterName))
                {
                    parameters = (string)actionContext.ControllerContext.RouteData.Values[parameterName];
                }
                else
                {
                    var queryString = actionContext.ControllerContext.Request.RequestUri.ParseQueryString();
                    if (queryString[parameterName] != null)
                    {
                        parameters = queryString[parameterName];
                    }
                }

                var values = parameters.Split(new[] { Separator }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(TypeDescriptor.GetConverter(type).ConvertFromString).ToArray();
                var typedValues = Array.CreateInstance(type, values.Length);
                values.CopyTo(typedValues, 0);
                actionContext.ActionArguments[parameterName] = typedValues;
            }
        }
    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        _ParameterNames.ForEach(parameterName => ProcessArrayInput(actionContext, parameterName));
    }
}

उपयोग:

    [HttpDelete]
    [ArrayInput("tagIDs")]
    [Route("api/v1/files/{fileID}/tags/{tagIDs}")]
    public HttpResponseMessage RemoveFileTags(Guid fileID, Guid[] tagIDs)
    {
        _FileRepository.RemoveFileTags(fileID, tagIDs);
        return Request.CreateResponse(HttpStatusCode.OK);
    }

उरी का अनुरोध करें

http://localhost/api/v1/files/2a9937c7-8201-59b7-bc8d-11a9178895d0/tags/BBA5CD5D-F07D-47A9-8DEE-D19F5FA65F63,BBA5CD5D-F07D-47A9-8DEE-D19F5FA65F63

@ इल्सा क्या आप कृपया बता सकते हैं कि कौन सा टुकड़ा आपको समझ नहीं आ रहा है? मुझे लगता है कि यह कोड स्वयं स्पष्ट करने के लिए काफी स्पष्ट है। मेरे लिए यह सब अंग्रेजी में समझाना कठिन है, क्षमा करें।
वानिनलेज़ू

@Steve Czetty यहाँ मेरा पुनर्निर्मित संस्करण है, आपके विचार के लिए धन्यवाद
वानिनलेज़ू

क्या यह /सेपरेटर के रूप में काम करेगा ? तब आपके पास हो सकता है: dns / root / mystuff / path / to / some / resource मैप किया गयाpublic string GetMyStuff(params string[] pathBits)
RoboJ1M

6

एक कस्टम ModelBinder का उपयोग करने के बजाय, आप TypeConverter के साथ एक कस्टम प्रकार का भी उपयोग कर सकते हैं।

[TypeConverter(typeof(StrListConverter))]
public class StrList : List<string>
{
    public StrList(IEnumerable<string> collection) : base(collection) {}
}

public class StrListConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        if (value == null)
            return null;

        if (value is string s)
        {
            if (string.IsNullOrEmpty(s))
                return null;
            return new StrList(s.Split(','));
        }
        return base.ConvertFrom(context, culture, value);
    }
}

लाभ यह है कि यह वेब एपीआई विधि के मापदंडों को बहुत सरल बनाता है। आपको [FromUri] को निर्दिष्ट करने की आवश्यकता नहीं है।

public IEnumerable<Category> GetCategories(StrList categoryIds) {
  // code to retrieve categories from database
}

यह उदाहरण स्ट्रिंग्स की सूची के लिए है, लेकिन आप categoryIds.Select(int.Parse)इसके बजाय केवल एक Intlist लिख या कर सकते हैं ।


समझ में नहीं आता कि इस समाधान को ज्यादा वोट क्यों नहीं मिले। यह अच्छा और साफ है और कस्टम बाइंडर और सामान को जोड़े बिना स्वैगर के साथ काम करता है।
थिएमे

मेरी राय में सबसे अच्छा / साफ जवाब। धन्यवाद फिलिप!
लेह बोवर्स

5

यदि आप पूर्णांक के सरणी / सरणी को सबसे आसान तरीके से सूचीबद्ध करना चाहते हैं, तो यह कॉमा (,) स्ट्रिंग की अलग सूची को स्वीकार करता है और इसे पूर्णांक की सूची में बदल देता है। आप [FromUri] attriubte.your यूआरएल का उल्लेख करना न भूलें जैसे:

...? आईडी = 71 & ACCOUNTID = 1,2,3,289,56

public HttpResponseMessage test([FromUri]int ID, [FromUri]string accountID)
{
    List<int> accountIdList = new List<int>();
    string[] arrAccountId = accountId.Split(new char[] { ',' });
    for (var i = 0; i < arrAccountId.Length; i++)
    {
        try
        {
           accountIdList.Add(Int32.Parse(arrAccountId[i]));
        }
        catch (Exception)
        {
        }
    }
}

तुम List<string>सिर्फ के बजाय क्यों इस्तेमाल करते हो string? इसमें केवल एक स्ट्रिंग होगी जो 1,2,3,289,56आपके उदाहरण में है। मैं एक संपादन का सुझाव दूंगा।
डैनियल टुलप

मेरे लिए काम किया। मुझे आश्चर्य था कि मेरा नियंत्रक List<Guid>स्वचालित रूप से यद्यपि नहीं होगा । Asp.net Core में नोट एनोटेशन है [FromQuery], और इसकी आवश्यकता नहीं है।
kitsu.eb

2
एक-लाइन लाइनक संस्करण के लिए: int [] accountIdArray = accountId.Split (',')। Select (i => int.Parse (i)। ToArray ()) मैं इसे पकड़ने से बचता हूँ क्योंकि यह किसी को खराब डेटा में पास करेगा।
सीओ

3

विधि प्रकार [HttpPost] बनाएं, एक मॉडल बनाएं जिसमें एक int [] पैरामीटर हो, और json के साथ पोस्ट करें:

/* Model */
public class CategoryRequestModel 
{
    public int[] Categories { get; set; }
}

/* WebApi */
[HttpPost]
public HttpResponseMessage GetCategories(CategoryRequestModel model)
{
    HttpResponseMessage resp = null;

    try
    {
        var categories = //your code to get categories

        resp = Request.CreateResponse(HttpStatusCode.OK, categories);

    }
    catch(Exception ex)
    {
        resp = Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
    }

    return resp;
}

/* jQuery */
var ajaxSettings = {
    type: 'POST',
    url: '/Categories',
    data: JSON.serialize({Categories: [1,2,3,4]}),
    contentType: 'application/json',
    success: function(data, textStatus, jqXHR)
    {
        //get categories from data
    }
};

$.ajax(ajaxSettings);

आप अपने एरे को एक कक्षा में लपेट रहे हैं - जो ठीक काम करेगा (एमवीसी / वेबएपीआई के बावजूद)। ओपी एक आवरण वर्ग के बिना सरणी के लिए बाध्यकारी था।
मचिफ

1
मूल समस्या रैपर क्लास के बिना इसे करने के बारे में कुछ भी नहीं कहती है, बस वे जटिल वस्तुओं के लिए क्वेरी परम का उपयोग करना चाहते थे। यदि आप उस रास्ते से बहुत नीचे चले जाते हैं, तो आपको एक ऐसे बिंदु पर जाना होगा जहाँ आपको वास्तव में जटिल js ऑब्जेक्ट लेने के लिए API की आवश्यकता होती है, और param की इच्छा आपको विफल कर देगी। हो सकता है कि यह हर बार काम करने का तरीका सीख ले।
कोडमोंकी

public IEnumerable<Category> GetCategories(int[] categoryIds){- हाँ, आप मुझे लगता है कि विभिन्न तरीकों से व्याख्या कर सकते हैं। लेकिन कई बार, मैं रैपर बनाने के लिए रैपर क्लास नहीं बनाना चाहता। यदि आपके पास जटिल ऑब्जेक्ट हैं, तो वह बस काम करेगा। इन सरल मामलों का समर्थन करना बॉक्स से बाहर काम नहीं करता है, इसलिए ओ.पी.
मर्चिफ़

3
इसके माध्यम से करना POSTवास्तव में अन्य प्रतिमानों के खिलाफ है। इस प्रकार ऐसे API एक REST API नहीं होंगे।
अजीमुथ

1
@ अज़ीमथ मुझे एक हाथ में एक प्रतिमान देते हैं, जो दूसरे में .NET के साथ काम करता है
कोडमोंकी

3

या आप केवल सीमांकित वस्तुओं की एक स्ट्रिंग को पारित कर सकते हैं और इसे प्राप्त अंत पर एक सरणी या सूची में डाल सकते हैं।


2

मैंने इस मुद्दे को इस तरह से संबोधित किया।

मैंने डेटा के रूप में पूर्णांकों की सूची भेजने के लिए एपीआई को एक पोस्ट संदेश का उपयोग किया।

फिर मैंने डेटा को एक ienumerable के रूप में वापस कर दिया।

भेजने का कोड इस प्रकार है:

public override IEnumerable<Contact> Fill(IEnumerable<int> ids)
{
    IEnumerable<Contact> result = null;
    if (ids!=null&&ids.Count()>0)
    {
        try
        {
            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri("http://localhost:49520/");
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                String _endPoint = "api/" + typeof(Contact).Name + "/ListArray";

                HttpResponseMessage response = client.PostAsJsonAsync<IEnumerable<int>>(_endPoint, ids).Result;
                response.EnsureSuccessStatusCode();
                if (response.IsSuccessStatusCode)
                {
                    result = JsonConvert.DeserializeObject<IEnumerable<Contact>>(response.Content.ReadAsStringAsync().Result);
                }

            }

        }
        catch (Exception)
        {

        }
    }
    return result;
}

प्राप्त कोड इस प्रकार है:

// POST api/<controller>
[HttpPost]
[ActionName("ListArray")]
public IEnumerable<Contact> Post([FromBody]IEnumerable<int> ids)
{
    IEnumerable<Contact> result = null;
    if (ids != null && ids.Count() > 0)
    {
        return contactRepository.Fill(ids);
    }
    return result;
}

यह सिर्फ एक रिकॉर्ड या कई रिकॉर्ड के लिए ठीक काम करता है। भराव DapperExtensions का उपयोग करके एक अतिभारित विधि है:

public override IEnumerable<Contact> Fill(IEnumerable<int> ids)
{
    IEnumerable<Contact> result = null;
    if (ids != null && ids.Count() > 0)
    {
        using (IDbConnection dbConnection = ConnectionProvider.OpenConnection())
        {
            dbConnection.Open();
            var predicate = Predicates.Field<Contact>(f => f.id, Operator.Eq, ids);
            result = dbConnection.GetList<Contact>(predicate);
            dbConnection.Close();
        }
    }
    return result;
}

यह आपको एक समग्र तालिका (आईडी सूची) से डेटा प्राप्त करने की अनुमति देता है, और फिर उन रिकॉर्ड को वापस कर देता है जिन्हें आप वास्तव में लक्ष्य तालिका से रुचि रखते हैं।

आप एक दृश्य के साथ भी ऐसा कर सकते हैं, लेकिन यह आपको थोड़ा अधिक नियंत्रण और लचीलापन देता है।

इसके अलावा, डेटाबेस से जो आप चाह रहे हैं उसका विवरण क्वेरी स्ट्रिंग में नहीं दिखाया गया है। आपको csv फ़ाइल से कनवर्ट करने की भी आवश्यकता नहीं है।

वेब एपीआई 2.x इंटरफ़ेस जैसे किसी भी टूल का उपयोग करते समय आपको यह ध्यान रखना होगा कि फ़ंक्शंस, पुट, पोस्ट, डिलीट, हेड इत्यादि का फ़ंक्शंस में एक सामान्य उपयोग होता है, लेकिन यह उस उपयोग के लिए प्रतिबंधित नहीं है।

इसलिए, जबकि पोस्ट आमतौर पर वेब एपीआई इंटरफेस में एक संदर्भ में उपयोग किया जाता है, यह उस उपयोग के लिए प्रतिबंधित नहीं है। यह एक नियमित html कॉल है जिसका उपयोग html अभ्यास द्वारा अनुमत किसी भी उद्देश्य के लिए किया जा सकता है।

इसके अलावा, जो कुछ चल रहा है उसका विवरण उन "चुभती आँखों" से छिपा है जो हम इन दिनों के बारे में बहुत कुछ सुनते हैं।

वेब एपीआई 2.x इंटरफ़ेस और नियमित वेब कॉलिंग के उपयोग में कन्वेंशनों के नामकरण में लचीलापन का मतलब है कि आप वेब एप को कॉल भेजते हैं जो स्नूपर्स को यह सोचने में गुमराह करता है कि आप वास्तव में कुछ और कर रहे हैं। उदाहरण के लिए, डेटा को पुनः प्राप्त करने के लिए आप "POST" का उपयोग कर सकते हैं।


2

मैंने एक कस्टम मॉडल बाइंडर बनाया है जो किसी भी अल्पविराम से अलग किए गए मानों (केवल आदिम, दशमलव, फ्लोट, स्ट्रिंग) को उनकी किरणों में परिवर्तित करता है।

public class CommaSeparatedToArrayBinder<T> : IModelBinder
    {
        public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
        {
            Type type = typeof(T);
            if (type.IsPrimitive || type == typeof(Decimal) || type == typeof(String) || type == typeof(float))
            {
                ValueProviderResult val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
                if (val == null) return false;

                string key = val.RawValue as string;
                if (key == null) { bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Wrong value type"); return false; }

                string[] values = key.Split(',');
                IEnumerable<T> result = this.ConvertToDesiredList(values).ToArray();
                bindingContext.Model = result;
                return true;
            }

            bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Only primitive, decimal, string and float data types are allowed...");
            return false;
        }

        private IEnumerable<T> ConvertToDesiredArray(string[] values)
        {
            foreach (string value in values)
            {
                var val = (T)Convert.ChangeType(value, typeof(T));
                yield return val;
            }
        }
    }

और नियंत्रक में उपयोग कैसे करें:

 public IHttpActionResult Get([ModelBinder(BinderType = typeof(CommaSeparatedToArrayBinder<int>))] int[] ids)
        {
            return Ok(ids);
        }

धन्यवाद, मैंने इसे netcore 3.1 में थोड़ा प्रयास के साथ चित्रित किया है और यह काम करता है! स्वीकृत उत्तर में समस्या को हल करने की आवश्यकता नहीं है, कई बार परम-नाम निर्दिष्ट करने की आवश्यकता है और यह नेटकोर 3.1 में डिफ़ॉल्ट ऑपरेशन के समान है
बोगडान मार्ट

0

मेरा समाधान स्ट्रिंग्स को मान्य करने के लिए एक विशेषता बनाने के लिए था, यह अतिरिक्त सामान्य सुविधाओं का एक समूह करता है, जिसमें रेगेक्स सत्यापन भी शामिल है जिसका उपयोग आप केवल संख्याओं की जांच के लिए कर सकते हैं और फिर बाद में आवश्यकतानुसार पूर्णांक में परिवर्तित कर सकते हैं ...

यह आप कैसे उपयोग करते हैं:

public class MustBeListAndContainAttribute : ValidationAttribute
{
    private Regex regex = null;
    public bool RemoveDuplicates { get; }
    public string Separator { get; }
    public int MinimumItems { get; }
    public int MaximumItems { get; }

    public MustBeListAndContainAttribute(string regexEachItem,
        int minimumItems = 1,
        int maximumItems = 0,
        string separator = ",",
        bool removeDuplicates = false) : base()
    {
        this.MinimumItems = minimumItems;
        this.MaximumItems = maximumItems;
        this.Separator = separator;
        this.RemoveDuplicates = removeDuplicates;

        if (!string.IsNullOrEmpty(regexEachItem))
            regex = new Regex(regexEachItem, RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase);
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var listOfdValues = (value as List<string>)?[0];

        if (string.IsNullOrWhiteSpace(listOfdValues))
        {
            if (MinimumItems > 0)
                return new ValidationResult(this.ErrorMessage);
            else
                return null;
        };

        var list = new List<string>();

        list.AddRange(listOfdValues.Split(new[] { Separator }, System.StringSplitOptions.RemoveEmptyEntries));

        if (RemoveDuplicates) list = list.Distinct().ToList();

        var prop = validationContext.ObjectType.GetProperty(validationContext.MemberName);
        prop.SetValue(validationContext.ObjectInstance, list);
        value = list;

        if (regex != null)
            if (list.Any(c => string.IsNullOrWhiteSpace(c) || !regex.IsMatch(c)))
                return new ValidationResult(this.ErrorMessage);

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