Как отправить список целых чисел в запрос на получение веб-API 2?

Я пытаюсь выполнить эту задачу, в которой мне нужно отправить список идентификаторов (целых чисел) в запрос на получение веб-API 2.

Итак, я нашел несколько примеров здесь и у него даже есть пример проекта, но он не работает...

Вот мой код метода веб-API:

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

и вот URL-адрес, который я тестирую в скрипаче:

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

Но список всегда пуст, и ни один из идентификаторов не проходит через метод.

не могу понять, проблема в методе, в URL или в обоих...

Итак, как это возможно осуществить?


person Liran Friedman    schedule 06.07.2016    source источник
comment
Вам понадобится связывание пользовательской модели, чтобы все заработало   -  person Aleksey L.    schedule 06.07.2016
comment
Как был отмечен этот ответ как правильно?   -  person Liran Friedman    schedule 06.07.2016
comment
Не могли бы вы продемонстрировать, как использовать эту модель связующего в моем случае?   -  person Liran Friedman    schedule 06.07.2016
comment
Попробуйте с 192.168.9.43/api/NewHotelData/   -  person Adrián López    schedule 06.07.2016
comment
Возможный дубликат: stackoverflow.com/questions/32336719/   -  person Honza Kalfus    schedule 06.07.2016
comment
Один из способов обхода состоит в том, чтобы на самом деле сделать его POST, а не получением - я знаю, что это противоречит тому, для чего они используются.   -  person Flexicoder    schedule 06.07.2016


Ответы (5)


Вам понадобится пользовательский модуль связывания моделей, чтобы заставить это работать. Вот упрощенная версия, с которой вы можете начать работу:

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

И используйте его таким образом (удалите {ids} из маршрута):

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

Если вы хотите сохранить {ids} в маршруте, вы должны изменить запрос клиента на:

api/NewHotelData/1,2,3,4

Другой вариант (без пользовательского связывателя модели) изменяет запрос на получение:

?ids=1&ids=2&ids=3
person Aleksey L.    schedule 06.07.2016
comment
Это правильно, но не удаляйте {ids} из маршрута. без него не работает. - person Liran Friedman; 06.07.2016
comment
Это работает, если вы используете его по запросу ?ids=1,2,3,4 - person Aleksey L.; 06.07.2016

Использование пользовательского связывателя модели, как предлагается в комментариях, является правильным способом сделать это. Тем не менее, вы также можете сделать это быстро и грязно, например так:

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

Сначала я разбиваю строку uri ids, а затем конвертирую их в список целых чисел с помощью Linq. Имейте в виду, что в нем отсутствуют проверки работоспособности, и если аргументы имеют неправильный формат, это приведет к ожиданиям.

Вы называете это так: http://192.168.9.43/api/NewHotelData?ids=5,10,20

Обновление: лично я думаю, что использование связывателя модели для такой простой вещи, как это, является чрезмерным проектированием. Вам нужно много кода, чтобы сделать эту простую работу. Код, который вы могли бы использовать в связывателе модели, на самом деле очень похоже, вы просто получите более приятный синтаксис аргумента метода. Если вы оберните целочисленный синтаксический анализ в блок try-catch и вернете соответствующее сообщение об ошибке в случае неправильного формата, я не вижу причин, почему бы не использовать этот подход.

person Honza Kalfus    schedule 06.07.2016
comment
Спасибо, но я знаком с этим способом. Если бы вы могли предоставить пример того, как использовать связыватель моделей для решения этой проблемы, было бы здорово :-) - person Liran Friedman; 06.07.2016
comment
Вы правы, и я могу с вами согласиться, но это требование, которое у меня есть, и мне нужно сделать это, как сказано :-) - person Liran Friedman; 06.07.2016
comment
Конечно, это ваше решение :) - person Honza Kalfus; 06.07.2016

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

Использование: http GET localhost:9000/api/getsomething?ids=1&ids=5&ids=9

person Pujubuju    schedule 06.07.2016
comment
Это работает, но мое требование состоит в том, чтобы метод вызывался без имени метода, например: localhost:9000/api/?ids=1&ids=5&ids=9, но это не работает... - person Liran Friedman; 06.07.2016

По-видимому, это может работать из коробки:

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

То есть повторение имени параметра с разными значениями. Но что произойдет, если эти идентификаторы станут огромными, и вам придется включать их много, что может сделать URL-адрес слишком длинным?

Я думаю, что было бы чище просто сделать запрос POST и покончить с этим. Вы написали, что вам нужно, чтобы запрос был GET, а не POST, но почему? Использование POST для получения вещей вполне приемлемо в контексте запросов AJAX.

person s.m.    schedule 06.07.2016
comment
@LiranFriedman хорошо, но работает ли повторение имени параметра? Вот что важно, все остальное — только мои собственные мысли. - person s.m.; 06.07.2016
comment
Я не уверен, потому что до сих пор на сервер не поступали никакие значения. - person Liran Friedman; 06.07.2016

Это может показаться слишком безвкусным, но можно также сделать что-то вроде:
В вашем классе (модели) .NET:

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

На стороне клиента вы бы сделали сообщение, например: {myIntList: [4,2,0]}

Теперь действие/метод на вашем контроллере будет выглядеть примерно так:

public void myApiMethodThatDoesSomethingWith(TackyList tl)
{
   // here you should be able to do something like:
   if(tl != null && tl.Count > 0) // blah
}
person E.P    schedule 24.04.2017