Добавление дубликатов параметров с 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
из NameValuePair
s в 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);