Маршрутизация Webapi с целочисленным массивом и действиями

Эта проблема сводит меня с ума.

У меня есть метод получения, который принимает массив идентификаторов с пользовательским ModelBinder (я могу вызвать http://xckvjl.com/api/results/1,23,34,)

Я хочу представить Gets по действиям. (чтобы я мог вызывать как http://alskjdfasl.com/api/results/latest)

У меня есть следующая маршрутизация веб-API.

config.Routes.MapHttpRoute("DefaultApi", "{controller}/{id}", new { id = RouteParameter.Optional });

config.Routes.MapHttpRoute("ApiWithAction", "{controller}/{action}");

Я пробовал с (Обратите внимание, что я использую свою собственную модель связывателя)

config.Routes.MapHttpRoute("DefaultApi", "{controller}/{id}", new { id = RouteParameter.Optional }, new {id = @"\d+" });

Вы можете воспроизвести эту ошибку с помощью этого примера:

public class TestController: ApiController {

          [HttpGet]
           public virtual IHttpActionResult Get([ModelBinder(typeof(CommaDelimitedCollectionModelBinder))]IEnumerable<int> id = null )
        { }

          [HttpGet]
           public virtual IHttpActionResult Latest( )
        { }

}

 public class CommaDelimitedCollectionModelBinder : IModelBinder
    {
        public bool BindModel(HttpActionContext actionContext,
            ModelBindingContext bindingContext)
        {
            var key = bindingContext.ModelName;
            var val = bindingContext.ValueProvider.GetValue(key);

            if (val == null)
            {
                return false;
            }

            var s = val.AttemptedValue;
            if (s != null && s.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Length > 0)
            {
                var array = s.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select( n=>Convert.ToInt32(n)).ToArray();
                Type type = bindingContext.ModelType.GetGenericArguments().First();

                var typeValue = Array.CreateInstance(type, array.Length);
                array.CopyTo(typeValue, 0);

                bindingContext.Model = array;
            }
            else
            {
                bindingContext.Model = new[] { s };
            }

            return true;
        }
    }

Если я напишу как:

[HttpGet]
[Route("Tests/latest")]
 public virtual IHttpActionResult Latest( )
        { }

Оно работает. Однако я хочу маршрутизацию на глобальном уровне. Иначе для каждого действия мне придется писать одно и то же.

Пожалуйста, порекомендуйте.


person codebased    schedule 26.09.2014    source источник


Ответы (1)


Итак, дилимма:

С этим определением маршрута:

config.Routes.MapHttpRoute("DefaultApi", "{controller}/{id}", new { id = RouteParameter.Optional });

когда вы вызываете http://alskjdfasl.com/api/results/latest, latest становится значением id, что, очевидно, вызовет ошибку, поскольку строка не может быть преобразована в массив int.

Добавление этого определения маршрута

config.Routes.MapHttpRoute("ApiWithAction", "{controller}/{action}"); 

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

Без объявления маршрутов на индивидуальном уровне единственными другими вариантами являются создание разных шаблонов или создание собственного ограничения маршрута, которое понимает массивы.

Я бы сначала попробовал эти:

config.Routes.MapHttpRoute("ApiWithAction", "{controller}/{action}/{id}", new { id = RouteParameter.Optional });

// or if all your methods are called latest

config.Routes.MapHttpRoute("ApiWithAction", "{controller}/latest");

Если ничего не работает, я бы сказал, используйте маршруты атрибутов. Они чище.

person Mrchief    schedule 26.09.2014
comment
Если я перевернусь, как вы предложили, это также приведет к множественному исключению маршрута. local/api/users/1 здесь становится запутанным, если 1 - это имя действия... так что я могу сделать ограничение для массива идентификаторов? Как? - person codebased; 26.09.2014
comment
Я пытаюсь понять дальше - какова цель маршрута ApiWithAction? Это то, что вы пытаетесь устранить при устранении неполадок? Кроме того, вы можете попробовать использовать int[] вместо IEnumerable? - person Mrchief; 26.09.2014
comment
Решение, которое я ищу, состоит в том, чтобы выставить такие действия, как Latest(..) в вышеуказанном контроллере. Поскольку существует более одного GET, мне придется сообщить маршруту, что когда я вызываю контроллер/действие, затем вызываю действие. ApiWitAction — это путь для этого. Если я не нахожу какой-либо маршрут и вызываю последний прямой, он все равно говорит, что найдено несколько действий, соответствующих запросу: я могу понять, почему это происходит, но я не знаю, как решить. - person codebased; 26.09.2014