在 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);
        }
    });
}