Java 在 Retrofit 中使用绝对 URL

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28116395/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 05:38:12  来源:igfitidea点击:

Using absolute URLs with Retrofit

javaandroidretrofit

提问by Kayvan N

I have a HALAPI that I'm working with and in many cases I need to send request (with different methods) to a URL that I get back from API. Meaning I don't want to hardcode the path of URL in my retrofit api interface but what I want is just sending a simple request using retrofit to that URL. I'm using Volley now and I know that I can use OkHttp for this purpose but I was wondering if there is a nice way of doing such thing in Retrofit?

我有一个正在使用的HALAPI,在许多情况下,我需要将请求(使用不同的方法)发送到我从 API 返回的 URL。这意味着我不想在我的改造 api 界面中硬编码 URL 的路径,但我想要的只是使用改造发送一个简单的请求到那个 URL。我现在正在使用 Volley,我知道我可以为此目的使用 OkHttp,但我想知道在 Retrofit 中是否有一种很好的方法来做这样的事情?

采纳答案by Kayvan N

Recently Square has released the Retrofit v2.0.0 BETAand it has a built-in support for dynamic URLs. Even though the Library is in Beta, based on what Jake Whartontold us in DroidCon NYC 2015, all the apis are stable and will not change. I'm personally adding it to my production so its up to you.

最近,Square 发布了Retrofit v2.0.0 BETA,它内置了对动态 URL 的支持。即使图书馆处于 Beta 版,根据Jake Wharton在 DroidCon NYC 2015 上告诉我们的内容,所有 api 都是稳定的,不会改变。我个人将它添加到我的作品中,所以这取决于你。

You will find the following links useful if you decide to do the upgrade:
Jake Wharton presentation @ DroidCon NYC 2015
A very good guide on the changes

如果您决定进行升级,您会发现以下链接很有用:
Jake Wharton 演讲 @ DroidCon NYC 2015
非常好的更改指南

In simple word, you can now use the api annotations (like @GET or @POST and others) without any path and then you pass in a @URL to your api method that the method will use to call.

简而言之,您现在可以在没有任何路径的情况下使用 api 注释(如 @GET 或 @POST 等),然后将 @URL 传递给该方法将用于调用的 api 方法。

----------------Retrofit 1.x

----------------改造 1.x

I figured out a nice way for doing this and would like to share it.

我想出了一个很好的方法来做到这一点,并想分享它。

The trick is to use the dynamic URL as your End Point in the creation of RestAdapter and then have a empty path on your API interface.

诀窍是在创建 RestAdapter 时使用动态 URL 作为端点,然后在 API 接口上有一个空路径。

Here is how I did it:

这是我如何做到的:

public RestAdapter getHostAdapter(String baseHost){
    RestAdapter restAdapter = new RestAdapter.Builder()
            .setEndpoint(baseHost)
            .setRequestInterceptor(requestInterceptor)
            .build();

    return restAdapter;
}

I build my restAdapter using this method and then I have this in my interface:
(this will not work if your URL has query parameters added to it. See next answer for solution to that case)

我使用这种方法构建了我的 restAdapter,然后我在我的界面中有这个:(
如果您的 URL 添加了查询参数,这将不起作用。请参阅下一个答案以了解该情况的解决方案)

public interface General {
    @GET("/")
    void getSomething(Callback<SomeObject> callback);
}

and finally using them like this:

最后像这样使用它们:

getHostAdapter("YOUR_DYNAMIC_URL").create(General.class)
    .getSomething(new Callback<SomeObject>(){
        ...
    })

Hope it helps.

希望能帮助到你。

回答by Kayvan N

In case that your URL has query parameters on it, the above solution will not work since it will add the '/' at the end of your base URL. for example if your URL is

如果您的 URL 上有查询参数,则上述解决方案将不起作用,因为它会在您的基本 URL 末尾添加“/”。例如,如果您的网址是

https://www.google.com/?q=test

then the above solution will try to send the request to

那么上面的解决方案将尝试将请求发送到

https://www.google.com/?q=test/

which will fail because of mall format.

由于商场格式,这将失败。

What we can do is one extra step and parsing the url. By parsing I mean just taking out all URL parameters and sending them in a QueryMap.

我们可以做的是一个额外的步骤并解析 url。解析我的意思是取出所有 URL 参数并将它们发送到QueryMap.

Here is how:

方法如下:

We should have the same structure describe above with a little change to our interface

我们应该有与上面描述的相同的结构,但对我们的界面稍作改动

public interface General {
    @GET("/")
    void getSomething(@QueryMap Map<String,String> queryMap, Callback<SomeObject> callback);
}

I just added a QueryMapto the above interface and now we can use this parser method:

我刚刚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);
}

now you can call this method like this:

现在你可以像这样调用这个方法:

getSomething("https://www.google.com/?q=test");

Enjoy coding.

享受编码。

Note: QueryMapwas added on Retrofit v1.4.0

注意:QueryMapRetrofit v1.4.0上添加

回答by garydossantos

I also need a path on my URL, so I did this:

我的 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;
    }

回答by praveena_kd

Adding to above two answers, Here is a working class that makes use of Queryparam and fires the absolute URL

添加以上两个答案,这是一个使用 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);
        }
    });

}

}

}