262

With Retrofit 2, you can set a full URL in the annotation of a service method like :

public interface APIService {
  @GET("http://api.mysite.com/user/list")
  Call<Users> getUsers();
}

However, in my app, the URL of my webservices are not known at compile time, the app retrieves them in a downloaded file so i'm wondering how i can use Retrofit 2 with full dynamic URL.

I tried to set a full path like :

public interface APIService {
  @GET("{fullUrl}")
  Call<Users> getUsers(@Path("fullUrl") fullUrl);
}

new Retrofit.Builder()
  .baseUrl("http://api.mysite.com/")
  .build()
  .create(APIService.class)
  .getUsers("http://api.mysite.com/user/list"); // this url should be dynamic
  .execute();

But here, Retrofit doesn't see that the path is actually a full URL and is trying to download http://api.mysite.com/http%3A%2F%2Fapi.mysite.com%2Fuser%2Flist

Any hint of how I could use Retrofit with such dynamic url ?

Thank you

pdegand59
  • 12,776
  • 8
  • 51
  • 58
  • 12
    For future searchers, there is a guide on dynamic URLs with Retrofit: https://futurestud.io/tutorials/retrofit-2-how-to-use-dynamic-urls-for-requests – peitek Apr 12 '17 at 08:01
  • same problem but way better answers in this thread https://stackoverflow.com/questions/53211672/using-retrofit-to-get-url-from-different-relative-paths – igor.beslic Mar 25 '21 at 21:02

9 Answers9

510

I think you are using it in wrong way. Here is an excerpt from the changelog:

New: @Url parameter annotation allows passing a complete URL for an endpoint.

So your interface should be like this:

public interface APIService {
    @GET
    Call<Users> getUsers(@Url String url);
}
JJD
  • 50,076
  • 60
  • 203
  • 339
Yazazzello
  • 6,053
  • 1
  • 19
  • 21
  • 1
    @pdegand59 It's brand new :) see [changelog](https://github.com/square/retrofit/blob/master/CHANGELOG.md) – Yazazzello Sep 14 '15 at 08:33
  • 2
    Why baseurl is required though the url for the endpoint is a fullurl? – Billyjoker Dec 07 '15 at 10:53
  • 19
    If I use this @Url annotation. Then What should we write in .baseUrl() ?? – Gulnaz Ghanchi Jun 14 '16 at 05:59
  • 18
    you should write your primary url. @Url parameter will override it anyway. – Yazazzello Jun 14 '16 at 14:30
  • Yazazzello For changing base url dynamically how to write @Url in APService? – Dhrupal Jul 20 '16 at 11:35
  • @Yazazzello can you please help me out in this question https://stackoverflow.com/questions/44018534/retrofit-2-errorjava-lang-illegalstateexception-expected-begin-object-but-was – Pratik Vyas May 25 '17 at 11:58
  • @GulnazGhanchi you must use it when for example one of your api get data from another server, it means you have two or more base url. – Amintabar May 12 '18 at 12:43
  • any way to append a suffix here itself, after @url ? – Shankara Narayana Jul 12 '18 at 11:44
  • 8
    I tried adding the URL like the one above but the baseUrl that i normally set for the retrofit builder overides the full url i specified in the parameter so instead of it being http://dynamic.com it gets translated to http://MyBaseUrl/http://dynamic.com – Jono Jul 17 '18 at 14:34
  • Can you still use Get("some/endpoint") with @Url? I want to have a base URL be dynamic but still use the retrofits parameters queries and so on. I don't want to manually construct them everytime – Nikola-Milovic May 21 '21 at 07:14
  • What if I don't know the Respone Model – Taimoor Khan Jul 17 '23 at 12:20
203

I wanted to replace only a part of the url, and with this solution, I don't have to pass the whole url, just the dynamic part:

public interface APIService {

  @GET("users/{user_id}/playlists")
  Call<List<Playlist> getUserPlaylists(@Path(value = "user_id", encoded = true) String userId);
}
Andras Kloczl
  • 8,415
  • 2
  • 21
  • 23
49

You can use the encoded flag on the @Path annotation:

public interface APIService {
  @GET("{fullUrl}")
  Call<Users> getUsers(@Path(value = "fullUrl", encoded = true) String fullUrl);
}
  • This will prevent the replacement of / with %2F.
  • It will not save you from ? being replaced by %3F, however, so you still can't pass in dynamic query strings.
chubao
  • 5,871
  • 6
  • 39
  • 64
fgysin
  • 11,329
  • 13
  • 61
  • 94
22

As of Retrofit 2.0.0-beta2, if you have a service responding JSON from this URL : http://myhost/mypath

The following is not working :

public interface ClientService {
    @GET("")
    Call<List<Client>> getClientList();
}

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("http://myhost/mypath")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

ClientService service = retrofit.create(ClientService.class);

Response<List<Client>> response = service.getClientList().execute();

But this is ok :

public interface ClientService {
    @GET
    Call<List<Client>> getClientList(@Url String anEmptyString);
}

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("http://myhost/mypath")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

ClientService service = retrofit.create(ClientService.class);

Response<List<Client>> response = service.getClientList("").execute();
yann-h
  • 521
  • 4
  • 7
20

You can use this :

@GET("group/{id}/users")

Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);

For more information see documentation https://square.github.io/retrofit/

Dev_Abraham
  • 404
  • 4
  • 6
10

Dynamic URL with Get and Post method in Retrofit (MVVM)

Retrofit Service interface:

public interface NetworkAPIServices {

@POST()
Observable<JsonElement> executXYZServiceAPI(@Url String url,@Body AuthTokenRequestModel param);


@GET
Observable<JsonElement> executeInserInfo(@Url String url);

MVVM service class:

   public Observable<JsonElement> executXYZServiceAPI(ModelObject object) {
    return networkAPIServices.authenticateAPI("url",
            object);
}

 public Observable<JsonElement> executeInserInfo(String ID) {
    return networkAPIServices.getBank(DynamicAPIPath.mergeUrlPath("url"+ID)));
}

and Retrofit Client class

 @Provides
@Singleton
@Inject
@Named("provideRetrofit2")
Retrofit provideRetrofit(@Named("provideRetrofit2") Gson gson, @Named("provideRetrofit2") OkHttpClient okHttpClient) {


   builder = new Retrofit.Builder();
    if (BaseApplication.getInstance().getApplicationMode() == ApplicationMode.DEVELOPMENT) {
        builder.baseUrl(NetworkURLs.BASE_URL_UAT);
    } else {
        builder.baseUrl(NetworkURLs.BASE_URL_PRODUCTION);
    }


    builder.addCallAdapterFactory(RxJava2CallAdapterFactory.create());
    builder.client(okHttpClient);
    builder.addConverterFactory(GsonConverterFactory.create(gson));


    return builder.build();
}

for example This is url : https://gethelp.wildapricot.com/en/articles/549-changing-your

baseURL : https://gethelp.wildapricot.com

Remaining @Url: /en/articles/549-changing-your (which is you pass in retro service class)

Vinod Makode
  • 953
  • 7
  • 7
7

Step-1

  Please define a method in Api interface like:-
 @FormUrlEncoded
 @POST()
 Call<RootLoginModel> getForgotPassword(
        @Url String apiname,
        @Field(ParameterConstants.email_id) String username
 );

Step-2 For a best practice define a class for retrofit instance:-

  public class ApiRequest {
       static Retrofit retrofit = null;



public static Retrofit getClient() {
    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);

    OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
            .addInterceptor(logging)
            .connectTimeout(60, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .build();

    if (retrofit==null) {
        retrofit = new Retrofit.Builder()
                .baseUrl(URLConstants.base_url)
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }
    return retrofit;
}

} Step-3 define in your activity:-

  final APIService request =ApiRequest.getClient().create(APIService.class);
  Call<RootLoginModel> call = request.getForgotPassword("dynamic api 
  name",strEmailid);
5

RetrofitHelper library written in kotlin, will let you make API calls, using a few lines of code and you can use different URLs, Headers and Params, in every Call.

Add multiple URLs in your application class like this :

class Application : Application() {

    override fun onCreate() {
    super.onCreate()

        retrofitClient = RetrofitClient.instance
                // api url
                .setBaseUrl("https://reqres.in/")
                // you can set multiple urls
                // .setUrl("example","http://ngrok.io/api/")
                // set timeouts
                .setConnectionTimeout(4)
                .setReadingTimeout(15)
                // enable cache
                .enableCaching(this)
                // add Headers
                .addHeader("Content-Type", "application/json")
                .addHeader("client", "android")
                .addHeader("language", Locale.getDefault().language)
                .addHeader("os", android.os.Build.VERSION.RELEASE)
            }

        companion object {
        lateinit var retrofitClient: RetrofitClient

        }
    }  

And then use the URL you need in your call:

retrofitClient.Get<GetResponseModel>()
            // set base url
            .setBaseUrlKey("example")
            // set path
            .setPath("api/users/2")
            // set url params Key-Value or HashMap
            .setUrlParams("KEY","Value")
            .setResponseHandler(GetResponseModel::class.java,
                object : ResponseHandler<GetResponseModel>() {
                    override fun onSuccess(response: Response<GetResponseModel>) {
                        super.onSuccess(response)
                        //handle response
                    }
                }).run(this)

For more information see the documentation

Arst
  • 3,098
  • 1
  • 35
  • 42
Mojtaba Razaghi
  • 319
  • 4
  • 10
4

If you already have your code setup and you don’t want to make changes on the different interfaces you can, use the solution described in this link. The main point is the method changeApiBaseUrl that updates the URL and recreates the Retrofit builder.

public class ServiceGenerator {  
public static String apiBaseUrl = "http://futurestud.io/api";
private static Retrofit retrofit;

private static Retrofit.Builder builder =
        new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(apiBaseUrl);

private static OkHttpClient.Builder httpClient =
        new OkHttpClient.Builder();

// No need to instantiate this class.
private ServiceGenerator() {
}

public static void changeApiBaseUrl(String newApiBaseUrl) {
    apiBaseUrl = newApiBaseUrl;

    builder = new Retrofit.Builder()
                    .addConverterFactory(GsonConverterFactory.create())
                    .baseUrl(apiBaseUrl);
}

public static <S> S createService(Class<S> serviceClass, AccessToken token) {
    String authToken = token.getTokenType().concat(token.getAccessToken());
    return createService(serviceClass, authToken);
}

// more methods
// ...
}

You can use it as follows:

public class DynamicBaseUrlActivity extends AppCompatActivity {

public static final String TAG = "CallInstances";
private Callback<ResponseBody> downloadCallback;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_file_upload);

    downloadCallback = new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            Log.d(TAG, "server contacted at: " + call.request().url());
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.d(TAG, "call failed against the url: " + call.request().url());
        }
    };

    // first request
    FileDownloadService downloadService = ServiceGenerator.create(FileDownloadService.class);
    Call<ResponseBody> originalCall = downloadService.downloadFileWithFixedUrl();
    originalCall.enqueue(downloadCallback);

    // change base url
    ServiceGenerator.changeApiBaseUrl("http://development.futurestud.io/api");

    // new request against new base url
    FileDownloadService newDownloadService = ServiceGenerator.create(FileDownloadService.class);
    Call<ResponseBody> newCall = newDownloadService.downloadFileWithFixedUrl();
    newCall.enqueue(downloadCallback);
    }
}