在 Retrofit 中使用绝对 URL
Using absolute URLs with Retrofit
我有一个正在使用的 HAL API,在许多情况下,我需要将请求(使用不同的方法)发送到我从中返回的 URL API。这意味着我不想在我的改造 api 界面中对 URL 的路径进行硬编码,但我想要的只是使用改造向 URL 发送一个简单的请求。
我现在正在使用 Volley,我知道我可以为此目的使用 OkHttp,但我想知道在 Retrofit 中是否有做这种事情的好方法?
最近 Square 在 DroidCon NYC 2015 上发布了 Retrofit v2.0.0 BETA and it has a built-in support for dynamic URLs. Even though the Library is in Beta, based on what Jake Wharton 告诉我们,所有 api 都是稳定的,不会改变。我亲自将它添加到我的作品中,所以由您决定。
如果您决定进行升级,您会发现以下链接很有用:
Jake Wharton presentation @ DroidCon NYC 2015
A very good guide on the changes
简而言之,您现在可以在没有任何路径的情况下使用 api 注释(如 @GET 或 @POST 等),然后将 @URL 传递给该方法将用于调用的 api 方法。
----------------改造1.x
我想出了一个很好的方法来做这件事并想分享它。
诀窍是在创建 RestAdapter 时使用动态 URL 作为端点,然后在 API 界面上有一个空路径。
我是这样做的:
public RestAdapter getHostAdapter(String baseHost){
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(baseHost)
.setRequestInterceptor(requestInterceptor)
.build();
return restAdapter;
}
我使用此方法构建我的 restAdapter,然后在我的界面中有此:
(如果您的 URL 添加了查询参数,这将不起作用。请参阅下一个答案以了解该案例的解决方案)
public interface General {
@GET("/")
void getSomething(Callback<SomeObject> callback);
}
最后像这样使用它们:
getHostAdapter("YOUR_DYNAMIC_URL").create(General.class)
.getSomething(new Callback<SomeObject>(){
...
})
希望对您有所帮助。
如果您的 URL 上有查询参数,上述解决方案将不起作用,因为它会在您的基础 URL 末尾添加“/”。例如,如果您的 URL 是
https://www.google.com/?q=test
然后上面的解决方案将尝试将请求发送到
https://www.google.com/?q=test/
由于商城格式,这将失败。
我们可以做的是多做一步并解析url。我所说的解析是指取出所有 URL 参数并将它们发送到 QueryMap
中。
方法如下:
我们应该具有与上面描述的相同的结构,只是对我们的界面稍作改动
public interface General {
@GET("/")
void getSomething(@QueryMap Map<String,String> queryMap, Callback<SomeObject> callback);
}
我刚刚在上面的接口中添加了一个QueryMap
,现在我们可以使用这个解析器方法:
public static void getSomething(@NonNull String urlString, @NonNull Callback<SomeObject> callback){
Uri uri = Uri.parse(urlString);
Set<String> queryParameterNames = uri.getQueryParameterNames();
String host = uri.getHost();
HashMap<String,String> queryMap = new HashMap<>();
Iterator<String> iterator = queryParameterNames.iterator();
while(iterator.hasNext()){
String queryName = iterator.next();
String queryParameter = uri.getQueryParameter(queryName);
queryMap.put(queryName, queryParameter);
}
getHostAdapter(host)
.create(General.class)
.getSomething(queryMap, callback);
}
现在您可以像这样调用此方法:
getSomething("https://www.google.com/?q=test");
享受编码。
注意:QueryMap
添加于 Retrofit v1.4.0
我的 URL 上还需要一个路径,所以我这样做了:
@GET("/{path}")
void getMatcherUrl(@Path(value = "path", encode = false) String path, @QueryMap Map<String, String> queryMap, RestCallback<MatcherResponse> matcherResponse);
/**
* Need to create a custom method because i need to pass a absolute url to the retrofit client
*
* @param urlString
* @param matcherResponse
*/
public void getMatcherUrl(@NonNull String urlString, @NonNull RestCallback<MatcherResponse> matcherResponse) {
Uri uri = Uri.parse(urlString);
Set<String> queryParameterNames = uri.getQueryParameterNames();
String host = uri.getHost();
String path = (uri.getPath().startsWith("/")) ? uri.getPath().substring(1) : uri.getPath();
HashMap<String, String> queryMap = new HashMap<>();
Iterator<String> iterator = queryParameterNames.iterator();
while (iterator.hasNext()) {
String queryName = iterator.next();
String queryParameter = uri.getQueryParameter(queryName);
queryMap.put(queryName, queryParameter);
}
getApiCoreService(host)
.getMatcherUrl(path, queryMap, matcherResponse);
}
public ApiCoreService getApiCoreService(String host) {
if (StringUtils.isEmpty(host))
this.endpoint = new RestEndpoint(RemoteConfigurationManager.getInstance().getApiCore(), "ApiCore");
else
this.endpoint = new RestEndpoint(host, "ApiCore");
return apiCoreService;
}
除了以上两个答案之外,这是一个有效的 class,它利用 Queryparam 并触发绝对值 URL
public class VideoClient {
private static final String TAG = "VideoCLient";
private final RestAdapter restAdapter;
private General apiService;
private String hostName;
private LinkedHashMap<String, String> queryMap;
private String Url_Path;
public VideoClient(String BASE_URL) {
Log.d(TAG,"Base url is "+BASE_URL);
hostName =getHostNameAndGenerateQueryMap(BASE_URL);
Gson gson = new GsonBuilder()
.create();
RequestInterceptor interceptor = new RequestInterceptor() {
@Override
public void intercept(RequestFacade request) {}
};
restAdapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint("http://"+hostName)
.setClient(new OkClient())
.setConverter(new GsonConverter(gson))
.setRequestInterceptor(interceptor)
.build();
private String getHostNameAndGenerateQueryMap(String urlString) {
Uri uri = Uri.parse(urlString);
Url_Path = (uri.getPath().startsWith("/")) ? uri.getPath().substring(1) : uri.getPath();
Set<String> queryParameterNames = uri.getQueryParameterNames();
String host = uri.getHost();
queryMap = new LinkedHashMap<>();
Iterator<String> iterator = queryParameterNames.iterator();
while (iterator.hasNext()) {
String queryName = iterator.next();
String queryParameter = uri.getQueryParameter(queryName);
Log.d(TAG,"query name "+queryName +" query param "+queryParameter);
queryMap.put(queryName, queryParameter);
}
return host;
}
public interface General {
/*void getVideo(@Path("auth_token") String authId,
@Query("et") String systemTime,@Query("service_id") String serviceID,
@Query("protocol") String scheme,@Query("play_url") String url,
@Query("us") String us,Callback<String> callback);
*/
@GET("/{path}")
getVideo(@Path(value="path", encode=false)String path,@QueryMap LinkedHashMap<String, String> queryMap);
}
public void getVideoDetails() {
Log.i("firing", "getVideoApi");
Log.d(TAG, "firing " + Url_Path + " function");
restAdapter.create(General.class).getVideo(Url_Path,queryMap, new Callback<Object>() {
@Override
public void success( Object o, Response response) {
Log.d(TAG," Success Response is "+response );
}
@Override
public void failure(RetrofitError error) {
Log.d(TAG, "Failure " + "Internal Error" + error);
}
});
}
我有一个正在使用的 HAL API,在许多情况下,我需要将请求(使用不同的方法)发送到我从中返回的 URL API。这意味着我不想在我的改造 api 界面中对 URL 的路径进行硬编码,但我想要的只是使用改造向 URL 发送一个简单的请求。 我现在正在使用 Volley,我知道我可以为此目的使用 OkHttp,但我想知道在 Retrofit 中是否有做这种事情的好方法?
最近 Square 在 DroidCon NYC 2015 上发布了 Retrofit v2.0.0 BETA and it has a built-in support for dynamic URLs. Even though the Library is in Beta, based on what Jake Wharton 告诉我们,所有 api 都是稳定的,不会改变。我亲自将它添加到我的作品中,所以由您决定。
如果您决定进行升级,您会发现以下链接很有用:
Jake Wharton presentation @ DroidCon NYC 2015
A very good guide on the changes
简而言之,您现在可以在没有任何路径的情况下使用 api 注释(如 @GET 或 @POST 等),然后将 @URL 传递给该方法将用于调用的 api 方法。
----------------改造1.x
我想出了一个很好的方法来做这件事并想分享它。
诀窍是在创建 RestAdapter 时使用动态 URL 作为端点,然后在 API 界面上有一个空路径。
我是这样做的:
public RestAdapter getHostAdapter(String baseHost){
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(baseHost)
.setRequestInterceptor(requestInterceptor)
.build();
return restAdapter;
}
我使用此方法构建我的 restAdapter,然后在我的界面中有此:
(如果您的 URL 添加了查询参数,这将不起作用。请参阅下一个答案以了解该案例的解决方案)
public interface General {
@GET("/")
void getSomething(Callback<SomeObject> callback);
}
最后像这样使用它们:
getHostAdapter("YOUR_DYNAMIC_URL").create(General.class)
.getSomething(new Callback<SomeObject>(){
...
})
希望对您有所帮助。
如果您的 URL 上有查询参数,上述解决方案将不起作用,因为它会在您的基础 URL 末尾添加“/”。例如,如果您的 URL 是
https://www.google.com/?q=test
然后上面的解决方案将尝试将请求发送到
https://www.google.com/?q=test/
由于商城格式,这将失败。
我们可以做的是多做一步并解析url。我所说的解析是指取出所有 URL 参数并将它们发送到 QueryMap
中。
方法如下:
我们应该具有与上面描述的相同的结构,只是对我们的界面稍作改动
public interface General {
@GET("/")
void getSomething(@QueryMap Map<String,String> queryMap, Callback<SomeObject> callback);
}
我刚刚在上面的接口中添加了一个QueryMap
,现在我们可以使用这个解析器方法:
public static void getSomething(@NonNull String urlString, @NonNull Callback<SomeObject> callback){
Uri uri = Uri.parse(urlString);
Set<String> queryParameterNames = uri.getQueryParameterNames();
String host = uri.getHost();
HashMap<String,String> queryMap = new HashMap<>();
Iterator<String> iterator = queryParameterNames.iterator();
while(iterator.hasNext()){
String queryName = iterator.next();
String queryParameter = uri.getQueryParameter(queryName);
queryMap.put(queryName, queryParameter);
}
getHostAdapter(host)
.create(General.class)
.getSomething(queryMap, callback);
}
现在您可以像这样调用此方法:
getSomething("https://www.google.com/?q=test");
享受编码。
注意:QueryMap
添加于 Retrofit v1.4.0
我的 URL 上还需要一个路径,所以我这样做了:
@GET("/{path}")
void getMatcherUrl(@Path(value = "path", encode = false) String path, @QueryMap Map<String, String> queryMap, RestCallback<MatcherResponse> matcherResponse);
/**
* Need to create a custom method because i need to pass a absolute url to the retrofit client
*
* @param urlString
* @param matcherResponse
*/
public void getMatcherUrl(@NonNull String urlString, @NonNull RestCallback<MatcherResponse> matcherResponse) {
Uri uri = Uri.parse(urlString);
Set<String> queryParameterNames = uri.getQueryParameterNames();
String host = uri.getHost();
String path = (uri.getPath().startsWith("/")) ? uri.getPath().substring(1) : uri.getPath();
HashMap<String, String> queryMap = new HashMap<>();
Iterator<String> iterator = queryParameterNames.iterator();
while (iterator.hasNext()) {
String queryName = iterator.next();
String queryParameter = uri.getQueryParameter(queryName);
queryMap.put(queryName, queryParameter);
}
getApiCoreService(host)
.getMatcherUrl(path, queryMap, matcherResponse);
}
public ApiCoreService getApiCoreService(String host) {
if (StringUtils.isEmpty(host))
this.endpoint = new RestEndpoint(RemoteConfigurationManager.getInstance().getApiCore(), "ApiCore");
else
this.endpoint = new RestEndpoint(host, "ApiCore");
return apiCoreService;
}
除了以上两个答案之外,这是一个有效的 class,它利用 Queryparam 并触发绝对值 URL
public class VideoClient {
private static final String TAG = "VideoCLient";
private final RestAdapter restAdapter;
private General apiService;
private String hostName;
private LinkedHashMap<String, String> queryMap;
private String Url_Path;
public VideoClient(String BASE_URL) {
Log.d(TAG,"Base url is "+BASE_URL);
hostName =getHostNameAndGenerateQueryMap(BASE_URL);
Gson gson = new GsonBuilder()
.create();
RequestInterceptor interceptor = new RequestInterceptor() {
@Override
public void intercept(RequestFacade request) {}
};
restAdapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint("http://"+hostName)
.setClient(new OkClient())
.setConverter(new GsonConverter(gson))
.setRequestInterceptor(interceptor)
.build();
private String getHostNameAndGenerateQueryMap(String urlString) {
Uri uri = Uri.parse(urlString);
Url_Path = (uri.getPath().startsWith("/")) ? uri.getPath().substring(1) : uri.getPath();
Set<String> queryParameterNames = uri.getQueryParameterNames();
String host = uri.getHost();
queryMap = new LinkedHashMap<>();
Iterator<String> iterator = queryParameterNames.iterator();
while (iterator.hasNext()) {
String queryName = iterator.next();
String queryParameter = uri.getQueryParameter(queryName);
Log.d(TAG,"query name "+queryName +" query param "+queryParameter);
queryMap.put(queryName, queryParameter);
}
return host;
}
public interface General {
/*void getVideo(@Path("auth_token") String authId,
@Query("et") String systemTime,@Query("service_id") String serviceID,
@Query("protocol") String scheme,@Query("play_url") String url,
@Query("us") String us,Callback<String> callback);
*/
@GET("/{path}")
getVideo(@Path(value="path", encode=false)String path,@QueryMap LinkedHashMap<String, String> queryMap);
}
public void getVideoDetails() {
Log.i("firing", "getVideoApi");
Log.d(TAG, "firing " + Url_Path + " function");
restAdapter.create(General.class).getVideo(Url_Path,queryMap, new Callback<Object>() {
@Override
public void success( Object o, Response response) {
Log.d(TAG," Success Response is "+response );
}
@Override
public void failure(RetrofitError error) {
Log.d(TAG, "Failure " + "Internal Error" + error);
}
});
}