Добавление дубликатов параметров с Retrofit v1.9.0

Здесь задан похожий вопрос, но мой случай немного другой.

Я пытаюсь сделать запрос, похожий на следующее:

http://www.example.com/abc?foo=def&foo=ghi&foo=jkl&bar=xyz

У меня есть две проблемы, которые усложняют ситуацию. Во-первых, повторный параметр (установка значений для "foo" несколько раз) препятствует использованию QueryMap (У меня нет возможности передавать значения в строке запроса по-другому, например, в виде массива). Во-вторых, параметры запроса, которые я использую, являются динамическими, поэтому я не могу использовать Query и предоставьте ему список значений для данного имени параметра, так как я не буду знать имена параметров, пока не сделаю запрос.

Код, который я пытаюсь обновить, использует более старую версию Retrofit, но каким-то образом он имеет концепцию QueryList который занял List из NameValuePair s передать параметры запроса в качестве имени (и их значения в качестве значения) и разрешить дублирование параметров. Я не вижу ссылки на retrofit.http.QueryList где-нибудь в истории исходного кода Retrofit или в Интернете, поэтому я не уверен, было ли это специальным дополнением в то время. В любом случае, я пытаюсь найти лучший способ воспроизвести эту функциональность в последней версии, поэтому любая помощь будет принята!

4 ответа

Решение

Чтобы продолжить, я смог решить эту проблему с помощью QueryMap и немного взломать. В основном я переворачиваю List из NameValuePairs в HashMap, где я проверяю, есть ли у меня ключ, и если я это сделаю, я добавляю новое значение для того же ключа к старому значению. Таким образом, по существу (ключ, значение) превратится в (ключ, значение и ключ = значение2). Таким образом, когда строка запроса построена, у меня будет key = value & key = value2, как требуется. Чтобы это работало, мне нужно обработать значение, кодирующее себя, чтобы дополнительные амперсанды и знаки равенства, которые я включаю в значение, не кодировались.

Итак HashMap построен из List как это:

public static HashMap<String, String> getPathMap(List<NameValuePair> params) {
    HashMap<String, String> paramMap = new HashMap<>();
    String paramValue;

    for (NameValuePair paramPair : params) {
        if (!TextUtils.isEmpty(paramPair.getName())) {
            try {
                if (paramMap.containsKey(paramPair.getName())) {
                    // Add the duplicate key and new value onto the previous value
                    // so (key, value) will now look like (key, value&key=value2)
                    // which is a hack to work with Retrofit's QueryMap
                    paramValue = paramMap.get(paramPair.getName());
                    paramValue += "&" + paramPair.getName() + "=" + URLEncoder.encode(String.valueOf(paramPair.getValue()), "UTF-8");
                } else {
                    // This is the first value, so directly map it
                    paramValue = URLEncoder.encode(String.valueOf(paramPair.getValue()), "UTF-8");
                }
                paramMap.put(paramPair.getName(), paramValue);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
    }

    return paramMap;
}

Тогда мой запрос выглядит так:

@GET("/api/")
List<Repo> listRepos(@QueryMap(encodeValues=false) Map<String, String> params);

И мой сервисный звонок выглядит так:

// Get the list of params for the service call
ArrayList<NameValuePair> paramList = getParams();

// Convert the list into a map and make the call with it
Map<String, String> params = getPathMap(paramList);
List<Repo> repos = service.listRepos(params);

Я изначально пробовал решение с помощью Path где я пытался вручную построить строку запроса, но блоки замены не допускаются в строке запроса, поэтому я выбрал это решение QueryMap. Надеюсь, это поможет всем, кто сталкивается с той же проблемой!

Интерфейс:

@GET()
Call<List<CustomDto>> getCustomData(@Url String url);

Запрос данных:

 String params = "custom?";
 for (Integer id : ids) {
      params += "id=" + id.toString() + "&";
 }
 params = params.substring(0, params.length() - 1);
 api.getCustomData(params).enqueue(callback);

Существует более простой способ добавления повторяющихся параметров, доступный в Retrofit 1.9/2+.

      public interface AbcService {  
    @GET("/abc")
    Call<List<Abc>> getAbcData(@Query("foo") List<String> foos, @Query("bar") String bar);
}

где foos можно определить следующим образом:

      List<String> foos = Arrays.asList("def", "ghi", "jkl");

для деталей вы можете проверить также эту ссылку

Просто передайте список своему @Query:

@GET("/api")
Response shareItem(@Query("email")List<String> email);
Другие вопросы по тегам