Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send a list of integers to web api 2 get request?

I am trying to accomplish this task in which I need to send a list of id's (integers) to a web api 2 get request.

So I've found some samples here and it even has a sample project, but it doesn't work...

Here is my web api method code:

[HttpGet]
[Route("api/NewHotelData/{ids}")]
public HttpResponseMessage Get([FromUri] List<int> ids)
{
    // ids.Count is 0
    // ids is empty...
}

and here is the URL which I test in fiddler:

http://192.168.9.43/api/NewHotelData/?ids=1,2,3,4

But the list is always empty and none of the id's are passing through to the method.

can't seem to understand if the problem is in the method, in the URL or in both...

So how this is possible to accomplish ?

like image 548
Liran Friedman Avatar asked Jul 06 '16 06:07

Liran Friedman


5 Answers

Using custom model binder as suggested in comments is the proper way to do this. However, you can also do it the quick-and-dirty way like so:

[HttpGet]
[Route("api/NewHotelData")]
public HttpResponseMessage Get([FromUri] string ids)
{
    var separated = ids.Split(new char[] { ',' });
    List<int> parsed = separated.Select(s => int.Parse(s)).ToList();
}

First, I'm splitting the uri ids string and then I'm converting them into a list of integers using Linq. Please beware that this is missing sanity checks and will throw expections if the arguments are in incorrect format.

You call it like this: http://192.168.9.43/api/NewHotelData?ids=5,10,20

Update: Personally, I think that using the model binder for a simple thing like this is over-engineering. You need a lot of code to make thing this simple work. The code you would use in a model binder is actually very similar, you would just get a nicer syntax of the method argument. If you wrap the integer parsing into a try-catch block and return an appropriate error message in case of bad format, I don't see a reason why not to use this approach.

like image 199
Honza Kalfus Avatar answered Nov 13 '22 19:11

Honza Kalfus


[HttpGet]
[Route("api/getsomething")]
public HttpResponseMessage Get([FromUri] params int[] ids)
{
}

Usage: http GET localhost:9000/api/getsomething?ids=1&ids=5&ids=9

like image 20
Pujubuju Avatar answered Nov 13 '22 20:11

Pujubuju


You'll need custom model binder to get this working. Here's simplified version you can start work with:

public class CsvIntModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        var key = bindingContext.ModelName;
        var valueProviderResult = bindingContext.ValueProvider.GetValue(key);
        if (valueProviderResult == null)
        {
            return false;
        }

        var attemptedValue = valueProviderResult.AttemptedValue;
        if (attemptedValue != null)
        {
            var list = attemptedValue.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries).
                       Select(v => int.Parse(v.Trim())).ToList();

            bindingContext.Model = list;
        }
        else
        {
            bindingContext.Model = new List<int>();
        }
        return true;
    }
}

And use it this way (remove {ids} from route):

[HttpGet]
[Route("api/NewHotelData")]
public HttpResponseMessage Get([ModelBinder(typeof(CsvIntModelBinder))] List<int> ids)

If you want to keep {ids} in route, you should change client request to:

api/NewHotelData/1,2,3,4

Another option (without custom model binder) is changing get request to:

?ids=1&ids=2&ids=3
like image 36
Aleksey L. Avatar answered Nov 13 '22 19:11

Aleksey L.


This might be too tacky, but one could also do something like:
In your .NET class (model):

public class TackyList
{
     public IEnumerable<int> myIntList {get; set;}
}

On the client side you'd do a post i.e: {myIntList: [4,2,0]}

Now the action/method on your controller would look something like:

public void myApiMethodThatDoesSomethingWith(TackyList tl)
{
   // here you should be able to do something like:
   if(tl != null && tl.Count > 0) // blah
}
like image 22
E.P Avatar answered Nov 13 '22 21:11

E.P


Apparently this could work out of the box:

http://192.168.9.43/api/NewHotelData/?ids=1&ids=2&ids=3&ids=4

That is, repeating the parameter name with the different values. But what would happen if those ids became huge and you had to include a lot of them, potentially making the URL too long?

I think it would be cleaner to just make it a POST request and be done with it, though. You wrote that you need the request to be a GET and not a POST, but why? Using POST to retrieve things is perfectly acceptable in the context of AJAX requests.

like image 1
s.m. Avatar answered Nov 13 '22 19:11

s.m.