使用 Retrofit v1.9.0 添加重复参数
Adding duplicate parameters with Retrofit v1.9.0
有人问过类似的问题here,但我的情况有点不同。
我正在尝试发出类似于以下内容的请求:
http://www.example.com/abc?foo=def&foo=ghi&foo=jkl&bar=xyz
我有两个问题让事情变得困难。首先,重复的参数(多次设置 "foo" 的值)阻止了 QueryMap
的使用(我没有选择以不同方式传递查询字符串中的值,比如作为数组) .其次,我使用的查询参数是动态的,所以我不能真正使用 Query
并为它提供给定参数名称的值列表,因为我不知道参数名称,直到我发出请求。
我尝试升级的代码使用的是旧版本的 Retrofit,但不知何故它有一个 QueryList
的概念,它采用了 List
的 NameValuePair
s将查询参数作为名称(及其值作为值)传递并允许重复参数。我在 Retrofit 的源代码历史记录或网络上的任何地方都没有看到对 retrofit.http.QueryList
的引用,所以我不确定当时这是否是自定义添加。无论如何,我正在尝试找到在最新版本中复制该功能的最佳方法,因此我们将不胜感激!
为了跟进,我能够通过使用 QueryMap
和一些 hack 来解决这个问题。基本上,我将 NameValuePair
的 List
转换为 HashMap,在其中检查我是否已经拥有密钥,如果已经拥有,我将同一密钥的新值附加到旧值。所以本质上 (key, value) 会变成 (key, value&key=value2)。这样,当构造查询字符串时,我将根据需要获得 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 解决方案。希望这对遇到同样问题的其他人有所帮助!
只需将列表传递给您的@Query:
@GET("/api")
Response shareItem(@Query("email")List<String> email);
接口:
@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");
有关详细信息,您还可以查看此 link
有人问过类似的问题here,但我的情况有点不同。
我正在尝试发出类似于以下内容的请求:
http://www.example.com/abc?foo=def&foo=ghi&foo=jkl&bar=xyz
我有两个问题让事情变得困难。首先,重复的参数(多次设置 "foo" 的值)阻止了 QueryMap
的使用(我没有选择以不同方式传递查询字符串中的值,比如作为数组) .其次,我使用的查询参数是动态的,所以我不能真正使用 Query
并为它提供给定参数名称的值列表,因为我不知道参数名称,直到我发出请求。
我尝试升级的代码使用的是旧版本的 Retrofit,但不知何故它有一个 QueryList
的概念,它采用了 List
的 NameValuePair
s将查询参数作为名称(及其值作为值)传递并允许重复参数。我在 Retrofit 的源代码历史记录或网络上的任何地方都没有看到对 retrofit.http.QueryList
的引用,所以我不确定当时这是否是自定义添加。无论如何,我正在尝试找到在最新版本中复制该功能的最佳方法,因此我们将不胜感激!
为了跟进,我能够通过使用 QueryMap
和一些 hack 来解决这个问题。基本上,我将 NameValuePair
的 List
转换为 HashMap,在其中检查我是否已经拥有密钥,如果已经拥有,我将同一密钥的新值附加到旧值。所以本质上 (key, value) 会变成 (key, value&key=value2)。这样,当构造查询字符串时,我将根据需要获得 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 解决方案。希望这对遇到同样问题的其他人有所帮助!
只需将列表传递给您的@Query:
@GET("/api")
Response shareItem(@Query("email")List<String> email);
接口:
@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");
有关详细信息,您还可以查看此 link