Retrofit 2 - Dynamic URL

AndroidRetrofit

Android Problem Overview


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

Android Solutions


Solution 1 - Android

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

Solution 2 - Android

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

Solution 3 - Android

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.

Solution 4 - Android

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();

Solution 5 - Android

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/

Solution 6 - Android

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)

Solution 7 - Android

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

Solution 8 - Android

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

Solution 9 - Android

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionpdegand59View Question on Stackoverflow
Solution 1 - AndroidYazazzelloView Answer on Stackoverflow
Solution 2 - AndroidAndras KloczlView Answer on Stackoverflow
Solution 3 - AndroidfgysinView Answer on Stackoverflow
Solution 4 - Androidyann-hView Answer on Stackoverflow
Solution 5 - AndroidDev_AbrahamView Answer on Stackoverflow
Solution 6 - AndroidVinod MakodeView Answer on Stackoverflow
Solution 7 - AndroidGovind ChauhanView Answer on Stackoverflow
Solution 8 - AndroidMojtaba RazaghiView Answer on Stackoverflow
Solution 9 - AndroidAbdelalim HassounaView Answer on Stackoverflow