Передать переменное количество параметров строки запроса в одно действие контроллера

Я создаю API с контроллером с одним действием GET:

[Route("api/xxxxx/{param1:int}/{param1:int}")]
public IHttpActionResult Get(int param1, int param2) {
    // method body...
}

URL-адреса будут в следующем формате:

/api/xxxxx/1/1?p1=5&p2=hello&p3=20161108
/api/xxxxx/1/1?p1=active

Количество и имена параметров строки запроса могут различаться.

Я хочу передать параметры строки запроса в метод контроллера, но я не могу жестко закодировать их в сигнатуре метода из-за разных имен и номеров. Есть ли способ сделать это? Я пытался вызвать var qsParams = ControllerContext.Request.GetQueryNameValuePairs();, но получаю сообщение об ошибке "ресурс не найден" при попытке запросить любой URL-адрес со строкой запроса с учетом показанного выше атрибута Route.

Я придумал одну альтернативу: использовать значения маршрута вместо параметров строки запроса, затем использовать универсальный {*tags} и передать его как параметр метода:

[Route("api/xxxxx/{param1:int}/{param1:int}/{*tags}")]
public IHttpActionResult Get(int param1, int param2, string tags) {
    // method body...
}

С URL-адресами в формате

/api/xxxxx/1/1/5/john/20161108
/api/xxxxx/1/1/active

Это работает, но я бы предпочел использовать строку запроса, чтобы иметь возможность использовать именованные ключи, вместо того, чтобы полагаться на порядок параметров (кроме того, использование строки запроса, похоже, лучше концептуально соответствует тому, что я делаю).

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

РЕДАКТИРОВАТЬ:

Стоит отметить, что создание нескольких методов действия для каждого возможного набора параметров не является вариантом.

РЕДАКТИРОВАТЬ 2:

Я вижу два прямых решения, если они возможны:

  1. Передайте всю строку запроса методу действия в виде одного строкового параметра. Затем я мог бы вручную проанализировать строку запроса.
  2. Иметь возможность использовать ControllerContext.Request.GetQueryNameValuePairs() внутри тела метода, НЕ добавляя соответствующие параметры в сигнатуру метода.

Тем не менее, я не понял, возможны ли эти две вещи, хотя вполне вероятно, что одна из них или обе.


person neizan    schedule 08.11.2016    source источник


Ответы (2)


Если вы хотите отправить объект на свой контроллер, вы можете получить его с помощью [FromBody]. Думайте, что объект — это самый простой способ передать его.

F.e.

[HttpPost]
[Route("api/xxxxx/send")]
public void SendReuqest([FromBody] Entity name)

Надеюсь правильно понять вашу проблему.

person Xivo    schedule 08.11.2016
comment
У меня нет POST, только GET. Кроме того, это, кажется, просто переносит проблему на сущность. То есть теперь фреймворк попытается сопоставить параметры с сущностью, но сущность должна будет определить параметры как свойства. Таким образом, объект должен иметь разные имена и количество свойств для сопоставления, что является той же проблемой, что и раньше, но теперь мы пытаемся решить ее в классе объектов. Кстати, я не использую Entity Framework. - person neizan; 08.11.2016

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

ОТВЕТ
Используйте метод GetQueryNameValuePairs. Это самое первое, что я попробовал, но у меня, должно быть, было что-то другое в атрибуте маршрута или сигнатуре метода, потому что раньше я получал ошибку «не найден». Однако теперь это работает отлично. Это делает весь этот вопрос и ответ в основном спорным для меня.

Пример URL:

/api/xxxxx/1/1?p1=5&p2=john&p3=20161108

Действие:

[Route("api/xxxxx/{param1:int}/{param1:int}")]
public IHttpActionResult Get(int param1, int param2) {
    var qsParams = ControllerContext.Request.GetQueryNameValuePairs();
    // rest of method body...
}

Вариант 1
Используйте один параметр строки запроса в пользовательском формате, который контроллер понимает и сможет анализировать.

Пример URL:

/api/xxxxx/1/1?qs=p1~5|p2~john|p3~20161108

Действие:

[Route("api/xxxxx/{param1:int}/{param1:int}")]
public IHttpActionResult Get(int param1, int param2, string qs) {
    string[] qsParamsArray = qs.Split(new string[] { "|" }, StringSplitOptions.RemoveEmptyEntries);
    IDictionary<string, string> qsParams = new Dictionary<string, string>();
    foreach (string p in qsParamsArray) {
        string[] kv = p.Split(new string[] { "~" }, StringSplitOptions.None);
        if (String.IsNullOrWhiteSpace(kv[0])) {
            return NotFound();
        }
        qsParams.Add(kv[0], kv[1]);
    }
    // Use qsParams as desired...
}

Вариант 2
Поместите параметры строки запроса в маршрут вместо строки запроса. Должен полагаться на порядок параметров.

Пример URL:

/api/xxxxx/1/1/5/john/20161108

Действие:

[Route("api/xxxxx/{param1:int}/{param1:int}/{*tags}")]
public IHttpActionResult Get(int param1, int param2, string tags) {
    // parse the "tags" parameter here...
    // rest of method body...
}

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

Пример URL:

/api/xxxxx/1/1?name1=p1&value1=5&name2=p2&value2=john&name3=p3&value3=20161108

Действие:

[Route("api/xxxxx/{param1:int}/{param1:int}")]
public IHttpActionResult Get(
    int param1, 
    int param2, 
    string name1 = null, 
    string value1 = null, 
    string name2 = null, 
    string value2 = null, 
    string name3 = null, 
    string value3 = null, 
    string name4 = null, 
    string value4 = null, 
    ..., 
    string nameNMAX = null, 
    string valueNMAX = null
) {
    // method body...
}
person neizan    schedule 08.11.2016