Retrofit - 更改BaseUrl

49

我有一个场景,需要使用相同的基础URL调用API,例如www.myAPI.com,但使用不同的baseUrl

我有一个通过Builder构建的Retrofit 2实例:

return new Retrofit.Builder()
    .baseUrl(FlavourConstants.BASE_URL)
    .addConverterFactory(GsonConverterFactory.create(gson))
    .client(okHttpClient)
    .build();

FlavourConstants.BASE_URL看起来像这样:

public static final String BASE_URL = "http://myApi.development:5000/api/v1/";

对于一些WebRequests,我必须调用同一个API,但对于另一些WebRequests,我必须从完全不同的BaseUrl调用它。在运行时如何更改Retrofit实例以指向不同的URL?

Retrofit实例没有.setBaseUrlsetter或任何类似的方法,因为它是通过Builder构建的。

有什么想法吗?


1
正如您已经提到的那样,Retrofit实例有点不可变(这就是构建器的用途)。因此,您需要为要设置的其他URL创建另一个实例。 - asgs
如果您没有使用Dagger2,则只需查看步骤1并将拦截器添加到HttpClient Builder对象中。 - Zeeshan Akhtar
10个回答

30

幸运的是,Retrofit为此提供了一个简单的解决方案:

public interface UserManager {  
    @GET
    public Call<ResponseBody> userName(@Url String url);
}

url 字符串应该指定你想要使用的完整 URL。


4
这仅适用于“GET”方法,不适用于HTTP方法(如“@POST”)。我已解决此问题,并在验证其有效性后发布答案。 - Subby
1
@Subby,你验证过它是否有效吗?你的答案在哪里? - Neon Warge
3
这不是解决原始问题的方法,它不会改变基本URL(同时还会污染API)。它保留了原始的基本URL,但允许您完全更改一个特定调用的端点 - 这是一种变通方法,但并没有解决手头的问题。(是的,我刚试过了,它可以工作) - milosmns
@Subby,为什么这个答案不能与 @POST 一起使用? - Bitwise DEVS
你能添加任何文档链接来展示@Url参数注释的范围和工作原理吗? - hc_dev

21

Retrofit 2.4,2019年5月

解决这个麻烦的两个简单方法是:

  1. 在保留基本URL的情况下,硬编码新的URL:

    @GET("http://example.com/api/")
    Call<JSONObject> callMethodName();
    
  2. 将新的URL作为参数传递,同时保留基本URL不变:

    @GET
    Call<JSONObject> callMethodName(@Url String url);
    

注意:这些方法适用于GET或POST。但是,如果您只需要使用与基本URL不同的一两个异常URL,那么此解决方案只是高效的。否则,就会在代码整洁方面变得有些混乱。

如果您的项目需要完全动态生成基本URL,则可以开始阅读此信息


21
同时,在Kotlin中定义基础URL时,也存在这样的一个技巧。 例如:
@FormUrlEncoded
@Headers("Accept: application/json")
@POST
suspend fun login(
    baseUrl: String,
    @Field("login") login: String,
    @Field("password") password: String
    @Url url: String = "$baseUrl/auth"
): ResponseAuth

它不起作用了。抛出:

java.lang.IllegalArgumentException: No Retrofit annotation found. (parameter #1)

唯一的方法是由Jake Wharton建议的https://github.com/square/retrofit/issues/2161#issuecomment-274204152

Retrofit.Builder()
    .baseUrl("https://localhost/")
    .create(ServerApi::class.java)
class DomainInterceptor : Interceptor {

    @Throws(Exception::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        return chain.proceed(
            request.newBuilder()
                .url(
                    request.url.toString()
                        .replace("localhost", "yourdomain.com:443")
                        .toHttpUrlOrNull() ?: request.url
                )
                // OR
                //.url(HttpUrl.parse(request.url().toString().replace("localhost", "yourdomain.com:443")) ?: request.url())
                .build()
        )
    }
}

9

使用新的URL构建一个新的Retrofit客户端实例

在运行时更改Retrofit基础URL最简单(但不是最高效)的方法是使用新的URL重新构建Retrofit客户端实例:

private Retrofit retrofitInstance = Retrofit.Builder()
    .baseUrl(FlavourConstants.BASE_URL)
    .addConverterFactory(GsonConverterFactory.create(gson))
    .client(okHttpClient)
    .build();
    
public void setNewBaseUrl(String url) {
   retrofitInstance = new Retrofit.Builder()
      .baseUrl(url)
      .addConverterFactory(GsonConverterFactory.create(gson))
      .client(okHttpClient).build();
}

// ... then use this client instance
retrofitInstance.create(ApiService.class);

使用 OkHttp 时,请添加一个具有新 URL 的拦截器

或者,如果您正在使用与 Retrofit 集成的 OkHttp,则可以在构建 OkHttp 客户端时添加拦截器。

例如,像这个示例 Gist 中 swankjesse/HostSelectionInterceptor.java 所示:

HostSelectionInterceptor hostInterceptor = new HostSelectionInterceptor();
hostInterceptor.setHost(newBaseUrl);

return new OkHttpClient.Builder()
    .addInterceptor(hostInterceptor)
    .build();

是的,拦截器似乎是最干净的方法。 - milosmns

8

当我遇到这个问题时,我使用了下面的函数。但是我很匆忙,我相信我必须使用另一个并且我正在使用 "retrofit2:retrofit:2.0.2"。

public static Retrofit getClient(String baseURL) {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl(baseURL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        } else {
            if (!retrofit.baseUrl().equals(baseURL)) {
                retrofit = new Retrofit.Builder()
                        .baseUrl(baseURL)
                        .addConverterFactory(GsonConverterFactory.create())
                        .build();
            }
        }
        return retrofit;
    }

[更新] 我发现了这个链接,它解释了可以作为参数发送的@Url,我相信这比我的旧解决方案更专业。 请参考以下场景:

    interface APIService{
         @POST
         Call<AuthenticationResponse> login(@Url String loginUrl,[other parameters])
    }

以下是提供Retrofit对象的类中的方法

public static Retrofit getClient() {
    if (retrofit==null) {
        retrofit = new Retrofit.Builder()
                .baseUrl("http://baseurl.com") // example url
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }
    return retrofit;
}

然后您可以按以下方式调用该方法:
    APIInterface apiInterface = ApiClient.getClient2().create(ApiInterface.class);
    apiInterface.login("http://tempURL.com").enqueue(......);

1
你应该像这样使用拦截器:
class HostSelectionInterceptor: Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        apiHost?.let { host ->
            val request = chain.request()
            val newUrl = request.url.newBuilder().host(host).build()
            val newRequest = request.newBuilder().url(newUrl).build()
            return chain.proceed(newRequest)
        }
        throw IOException("Unknown Server")
    }
}

You just need to change at runtime the apiHost variable (var apiHost = "example.com"). Then add this interceptor to OkHttpClient builder:

val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(HostSelectionInterceptor())
    .build()

这是最好的答案。它运行良好。谢谢! - Roshan
这并不是很好,因为如果您添加了HttpLogginInterceptor,它将始终记录您最初提供的基本URL,而不是新主机。 - Jokubas Trinkunas
这个答案有三个问题:(1) 看不到在你的代码示例中修改apiHost的位置。(2) OP要求Java,而这个解决方案是Kotlin。(3) _host-interceptor_已经被Phileo99回答了。 - hc_dev

1

好的,如果我没记错,Retrofit的文档说如果你在接口服务中添加完整的Web服务URL,就可以指向另一个URL,这与Retrofit Builder中的BASE_URL不同。一个例子...

public interface UserManager {  
    @GET("put here ur entire url of the service")
    public Call<ResponseBody> getSomeStuff();
}

0
一个解决方案是拥有两个不同的Retrofit实例,一个用于您的FLAVOURED基础URL,另一个用于其他基础URL。
因此,只需定义两个函数:
     public Retrofit getFlavouredInstance() {
        return new Retrofit.Builder().baseUrl(FlavourConstants.BASE_URL).addConverterFactory(GsonConverterFactory.create(gson)).client(okHttpClient).build();
     }

     public Retrofit getOtherBaseUrl() {
        return Retrofit.Builder().baseUrl(OTHER_BASE_URL).addConverterFactory(GsonConverterFactory.create(gson)).client(okHttpClient).build();
     }

之后你只需要使用正确的一个即可。


0
请尝试以下代码:
private void modify(String url) throws Exception {

    Class mClass = retrofit.getClass();


    Field privateField = mClass.getDeclaredField("baseUrl");

    if (privateField != null) {
        privateField.setAccessible(true);

        System.out.println("Before Modify:MSG = " + retrofit.baseUrl().url().getHost());


        privateField.set(retrofit,  HttpUrl.parse(url));
        System.out.println("After Modify:MSG = " + retrofit.baseUrl().url().getHost());
    }
}

-1
您可以在更改 apiUrl 后重新生成 DaggerAppComponent,它将使用新的 url 生成 providerRetrofit 的新实例。 DaggerAppComponent.builder() .application(this) .build() Log.init( LogConfiguration .Builder() .tag("...") .logLevel(LogLevel.NONE) .build() )

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接