I am using the following libraries for retrofit.
'com.squareup.retrofit2:retrofit:2.5.0'
'com.squareup.okhttp:okhttp:2.7.5'
'com.squareup.retrofit2:converter-gson:2.5.0'
'com.squareup.okhttp3:logging-interceptor:3.10.0'
How can I get the raw response in onResponse callback? I already searched for it and got a lots of solutions which doesn't help now. I tried response.raw().body.string() and response.body().source().toString() which throws can not read body from a converted body. I also tried response.body().string() but in this case .string() is unresolved. I can log the response using interceptor but I need that response in my onResponse() callback, not just printing in logcat.
My Retrofit Client:
public static ApiService getClient(Context context) {
if (retrofit == null) {
if (BuildConfig.FLAVOR.equalsIgnoreCase("dev")){
retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.client(okHttpClient)
.build();
}else {
retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.client(SelfSigningClientBuilder.createClient(context))
.build();
}
}
return retrofit.create(ApiService.class);
}
My Retyrofit Interface:
#retrofit2.http.POST("weekly_driver_earning_report/")
#retrofit2.http.FormUrlEncoded
Call<List<DailyEarnings>> getDriverWeeklyEarnings(#retrofit2.http.Field("access_token") String access_token, #retrofit2.http.Field("start_date") String start_date, #retrofit2.http.Field("end_date") String end_date);
Following this, I solved my problem later. I defined my Call type to specific model class and that's why response.body().string() was inaccessible, Changing my Call type from List to ResponseBody, I could access response.body().string().
Related
I have a scenario where I have to call an API with the same base URL, e.g. www.myAPI.com but with a different baseUrl.
I have an instance of Retrofit 2 which is built via a Builder:
return new Retrofit.Builder()
.baseUrl(FlavourConstants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.client(okHttpClient)
.build();
The FlavourConstants.BASE_URL looks like this:
public static final String BASE_URL = "http://myApi.development:5000/api/v1/";
For some WebRequests, I must call the same API but on others, I must call it from a completely different BaseUrl. How do I change the Retrofit instance to therefore point to a different URL during runtime?
The Retrofit instance doesn't have a .setBaseUrl or setter or anything similar as it's built via a Builder.
Any ideas?
Lucky for you Retrofit have a simple solution for that:
public interface UserManager {
#GET
public Call<ResponseBody> userName(#Url String url);
}
The url String should specify the full Url you wish to use.
Retrofit 2.4, MAY 2019
Two simple solution for this hassle are:
Hardcode the new URL, while leaving the base URL as it is:
#GET("http://example.com/api/")
Call<JSONObject> callMethodName();
Pass the new URL as an argument, while leaving the base URL as it is:
#GET
Call<JSONObject> callMethodName(#Url String url);
N.B: These methods work for GET or POST. However, this solution is only efficient if you just need to use an exception of one or two different URLs than your base URL. Otherwise, things can get a little messy in terms of code neatness.
If your project demands fully dynamically generated base URLs then you can start reading this.
Also there is a such hack in Kotlin while defining base url
e.g.
#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
It's not working. Throws:
java.lang.IllegalArgumentException: No Retrofit annotation found. (parameter #1)
The only way is suggested by 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()
)
}
}
The easiest (but not the most performant) way to change the Retrofit2 base URL at runtime is to rebuild the retrofit instance with the new url:
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();
}
...
retrofitInstance.create(ApiService.class);
Alternatively, if you are using OkHttp with Retrofit, you can add an OkHttp interceptor like this one when building your OkHttp client:
HostSelectionInterceptor hostInterceptor = new HostSelectionInterceptor();
hostInterceptor.setHost(newBaseUrl);
return new OkHttpClient.Builder()
.addInterceptor(hostInterceptor)
.build();
I just used the below function when i faced this problem. but i was on hurry and i believe that i have to use another and i was using "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;
}
[Update]
I have found this link that explain the #Url that can be sent as a parameter and i believe it is more professional than my old solution.
Please find below the scenario:
interface APIService{
#POST
Call<AuthenticationResponse> login(#Url String loginUrl,[other parameters])
}
And below is the method in the class that provide the retrofit object
public static Retrofit getClient() {
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl("http://baseurl.com") // example url
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
Then you can call the method as below:
APIInterface apiInterface = ApiClient.getClient2().create(ApiInterface.class);
apiInterface.login("http://tempURL.com").enqueue(......);
You should use interceptor like this:
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()
Ok , if I dont remember wrong the docs of Retrofit says you can point to another URL if you just simply add in your interface servicse the full url of the ws, that is different fomr the BASE_URL in Retrofit Builder. One example...
public interface UserManager {
#GET("put here ur entire url of the service")
public Call<ResponseBody> getSomeStuff();
}
A solution is to have two distinct instance of retrofit, one for your FLAVOURED base URL and another for the other base URL.
So just define two functions :
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();
}
and after you just have to use the right one.
Please try the following code:
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());
}
}
You can regenerate the DaggerAppComponent after changing your apiUrl it will generate a new instance of providerRetrofit with the new url
DaggerAppComponent.builder() .application(this) .build() Log.init( LogConfiguration .Builder() .tag("...") .logLevel(LogLevel.NONE) .build() )
This question already has an answer here:
Retrofit encoding special characters
(1 answer)
Closed 3 years ago.
I'm having this weird error when using Retrofit.
First of all I tried using okhttpClient just for comparison and im getting the json result as expected.
val requestBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("email", "my.email#email.com")
.build()
val request = Request.Builder()
.url(BASE_URL + "account/forgot")
.post(requestBody)
.build()
var client = OkHttpClient()
client.newCall(request).execute()
.use { response ->
val response = response.body()!!.string()
}
Which returns
{"success": true, "email": "my.email#email.com", "uu_id": "000-0--0-0-000"}
Now Using the same logic, I tried converting it to retrofit but skip the GSON conversion as it returns unexpected error saying "JSON is not formatted"
so what I did was on callback, just return it as ResponseBody based on Retrofit's Documentation
#Headers("token: ", "accept-language: en-US", "accept: application/json", "accept-encoding: gzip, deflate, br", "Content-Type: application/json")
#POST("account/forgot")
fun resetPasswordDetails(#Body body:String): Call<ResponseBody>
And uses this RetrofitInstance
public static Retrofit getRetrofitInstance() {
Gson gson = new GsonBuilder()
.setLenient()
.create();
CookieManager cookieManager = new CookieManager();
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient defaultHttpClient = new OkHttpClient.Builder()
.cookieJar(new JavaNetCookieJar(cookieManager))
.addInterceptor(loggingInterceptor)
.addInterceptor(new ResponseInterceptor())
.build();
if (retrofit == null) {
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(BASE_URL)
.client(defaultHttpClient)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
return retrofit;
}
on my Main activity I used it as
val service = RetrofitClientInstance.getRetrofitInstance().create(GetDataService::class.java)
val jsonBody = JSONObject()
jsonBody.put("email", "my.email#email.com")
val call = service.resetPasswordDetails(jsonBody.toString())
val response = call.execute()
val value = response.body()?.string()
I'm expecting the same result as what I did on okHttp but the return string was
���������������-�A
�0E�Rf)M1mc�+o"���)�ED�{��>��>PW"�.ݳ��w��Q����u�Ib�ȃd���x�/\r���#95s)�Eo���h�S����jbc���̚���� �������
Is there something wrong on my retrofit instance? Why is that it is working on okhttp but not on retrofit
EDIT:
My question is tagged as duplicate but I dont think thats the same question. While the other one states that the problem relates to URL encoding, My question is why is the okhttpclient and retrofit doesn't return the same JSON
Based on Xavier Rubio Jansana comment, I deleted some of my headers and now it is working properly. I just retain the #Headers("Content-Type: application/json").. Thanks a lot sir
It might be that you are sending the request as a JSON Body instead of Multipart like your OkHTTP request.
To make a Multipart request you can define your Retrofit request like this:
#POST("account/forgot")
fun resetPasswordDetails(#Part email:String): Call<ResponseBody>
Then you can just call the method with the email address without creating any JSONObject.
I have sent and retrieved a url String from a json response using Parcelable in a fragment like so
Received String value
String profile_url = student.getProfile();
I want to use this string value as the basUrl to make another request using Retrofit like so
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(profile_url)
.addConverterFactory(GsonConverterFactory.create())
.build();
But getting the following error
java.lang.RuntimeException: An error occured while executing doInBackground()
......
Caused by: java.lang.IllegalArgumentException: baseUrl must end in /:
How do i put the / at the end of the baseUrl?
Putting it directly like so
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(profile_url/)
.addConverterFactory(GsonConverterFactory.create())
.build();
does not work, Express expected.
Any help. Thanks
Debug Profile url profile_url=http://services.hanselandstudent.com/student.svc/1.1/162137/category/1120/json?banner=0&atid=468f8dc2-9a38-487e-92ae-a194e81809d9
Since you've a complete url to call, you will need the #Url annotation in Retrofit2.
Define your interface with something like that
public interface YourApiService {
#GET
Call<Profile> getProfile(#Url String url);
class Factory {
public static YourApiService createService() {
Retrofit retrofit = new Retrofit.Builder()
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl("http://www.what.com/")
.build();
return retrofit.create(YourApiService.class);
}
}
}
Then call it with
YourApiService.createService().getProfile(profile_url);
You must have "/" in the end of your string variable.
String profile_url = student.getProfile() + "/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(profile_url)
.addConverterFactory(GsonConverterFactory.create())
.build();
Is there any possibility to compare a Call URL with a String in Retrofit 2?
For example we can take this baseUrl:
https://www.google.com
And this Call:
public interface ExampleService {
#GET("dummy/{examplePartialUrl}/")
Call<JsonObject> exampleList(#Path("examplePartialUrl") String examplePartialUrl;
}
with this request:
Call<JsonObject> mCall = dummyService.exampleList("partialDummy")
There's a way to obtain https://www.google.com/dummy/partialDummy or also dummy/partialDummy before getting the response from the call?
Assuming you're using OkHttp alongside Retrofit, you could do something like:
dummyService.exampleList("partialDummy").request().url().toString()
which according to the OkHttp docs should print:
https://www.google.com/dummy/partialDummy
Log.d(TAG, "onResponse: ConfigurationListener::"+call.request().url());
Personally I found another way to accomplish this by using retrofit 2 and RxJava
First you need to create an OkHttpClient object
private OkHttpClient provideOkHttpClient()
{
//this is the part where you will see all the logs of retrofit requests
//and responses
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
return new OkHttpClient().newBuilder()
.connectTimeout(500, TimeUnit.MILLISECONDS)
.readTimeout(500,TimeUnit.MILLISECONDS)
.addInterceptor(logging)
.build();
}
after creating this object, the next step is just use it in retrofit builder
public Retrofit provideRetrofit(OkHttpClient client, GsonConverterFactory convertorFactory,RxJava2CallAdapterFactory adapterFactory)
{
return new Retrofit.Builder()
.baseUrl(mBaseUrl)
.addConverterFactory(convertorFactory)
.addCallAdapterFactory(adapterFactory)
.client(client)
.build();
}
one of the attributes you can assign to Retrofit builder is the client, set the client to the client from the first function.
After running this code you could search for OkHttp tag in the logcat and you will see the requests and responses you made.
I have to call an api using retrofit 2 in android. but with no values. When I does that, it shows that there must be at least 1 #field. Below is the code I am using.
public interface gitAPI {
#FormUrlEncoded
#POST("/MembersWS.svc/GetMemberAvailability/MBR0011581")
Call<Questions[]> loadQuestions();
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.1.99:82")
.addConverterFactory(GsonConverterFactory.create())
.build();
// prepare call in Retrofit 2.0
gitAPI stackOverflowAPI = retrofit.create(gitAPI.class);
Call<Questions[]> call = stackOverflowAPI.loadQuestions();
call.execute();
Declare body value in your interface with next:
#Body RequestBody body and wrap String JSON object:
RequestBody body = RequestBody.create(MediaType.parse("application/json"), (new JsonObject()).toString());