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.
Related
var client = OkHttpClient()
val builder = OkHttpClient.Builder()
val gson = GsonBuilder()
.setLenient()
.create()
builder.addInterceptor(AddCookiesInterceptor(mcontext))
builder.addInterceptor(ReceivedCookiesInterceptor(mcontext))
builder.callTimeout(100,TimeUnit.SECONDS)
client = builder.build()
retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
// .addConverterFactory(ScalarsConverterFactory.create())
.build()
This is my retrofit client service builder. For normal api functions with json response callback it works fine. Is there any modification required for an large file upload?
With current scenario, uploading 20 mb data, takes more time in slow network connection which returns a socket timeout exception.
Uploading as multipart body
var fileBody : ProgressRequestBody? = null
fileBody = ProgressRequestBody(file,"*/*",this#CaseFileUploadFragment)
var fileToUpload: MultipartBody.Part =
MultipartBody.Part.createFormData("image",file.name, fileBody)
var filename : RequestBody =
RequestBody.create(MediaType.parse("text/plain"),file.getName())
and following is the function used
#Multipart
#POST("{urlMpString}")
fun uploadFile(
#Path ("urlMpString") urlEndString : String, #Part file: MultipartBody.Part, #Part("file") requestBody: RequestBody,
#Part("apiInfo") `object1`: JsonObject, #Part("parameters") `object2`: JsonObject
): Call<JsonObject>
Everything works fine for small data files.
Any suggestions and help will be appreciated.
Thanks
Try below code for okHttpClient
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.MINUTES)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.build();
I've tried sending the token with a HeaderMap but get a 401 code response. The way my project is setup is that I have a separate file for my ApiClient and I have a OkHttpClient Interceptor and a HttpLoggingInterceptor to see whats going on, however I can't get the Bearer Token to work. I've seen solutions that add it to the interceptor as a header in the interceptor and I've tried this but since my token is saved in SharedPreferences I can't get it to work in the ApiClient class I have.
This is the ApiClient
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
Gson gson = new GsonBuilder().serializeNulls().setLenient().create();
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
#NotNull
#Override
public okhttp3.Response intercept(#NotNull Chain chain) throws IOException {
Request originalRequest = chain.request();
Request newRequest = originalRequest.newBuilder()
//I would add the header here
//I tried this but it says on "ApiClient.this" cannot be referenced from static context
// .header("Authorization" , SharedPreferencesHelper.getUserToken(ApiClient.this));
.build();
return chain.proceed(newRequest);
}
})
.addInterceptor(interceptor)
.build();
retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.0.6:8000/api/")
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
This is the method from SharedPreferencesHelper.getUserToken(MainActivity.this)
public static String getUserToken(Context context) {
SharedPreferences sp = getSharedPreferences(context);
return sp.getString(USER_TOKEN, null);
}
This is the current call where the response is 401, If I don't add the Accept => application/json the response url is incorrect and also returns a html page when I need a simple response return response("LoggedOut", 200); //this is the response in the api
Map<String, String> headers = new HashMap<>();
headers.put("Accept", "application/json");
headers.put("Token", SharedPreferencesHelper.getUserToken(MainActivity.this));
Call<Void> call = apiInterface.LogoutUser(headers);
call.enqueue(new Callback<Void>() {
// onResponse and onFailure here
}
For example without the Accept header this is the response in the Logcat
D/OkHttp: --> GET http://192.168.0.6:8000/api/logout
D/OkHttp: Token: wE1Y8IxJpwyXtvw0fYoXZAlQ6qCx24YtzonQIeJBQSHmNppe0Sn1kLYDgZKCw4MKbpab4Vspf61Nzer1
D/OkHttp: --> END GET
D/OkHttp: <-- 200 OK http://192.168.0.6:8000/login
//a bunch of html that's the web page at this route, notice the /api is missing
How can I send this correctly?
EDIT:
I"m using a Laravel project for the backend and this is the relevant route
Route::middleware('auth:sanctum')
->get('/logoutApi', function (Request $request) {
$request->user()->tokens()->delete();
return response("LoggedOut", 202);
});
create class Authenticator, like:
const val HEADER_TOKEN_FIELD = "Authorization"
class ClassAuthenticator(
private val pref: SharedPref
) : Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
return response.request().newBuilder()
.header(HEADER_TOKEN_FIELD, pref.getToken())
.build()
}
}
then add interceptor in your client with:
val httpClient = OkHttpClient.Builder()
.authenticator(ClassAuthenticator(pref))
.addInterceptor { chain ->
val request = chain.request()
val httpRequest = request.newBuilder()
.addHeader(HEADER_TOKEN_FIELD,
"Bearer ${pref.getToken()}")
.build()
val response = chain.proceed(httpRequest)
response
}
.build()
In my application i want get data from server and for this i should add some header such as Accept and Content_Type .
For connect to server i used Retrofit library.
For set headers i use okHttp client and i write below codes, but not set header to api response!
My Client codes:
class ApiClient() {
private val apiServices: ApiServices
init {
//Gson
val gson = GsonBuilder()
.setLenient()
.create()
//Http log
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.level =
if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
//Http Builder
val clientBuilder = OkHttpClient.Builder()
clientBuilder.interceptors().add(loggingInterceptor)
clientBuilder.addInterceptor { chain ->
val request = chain.request()
request.newBuilder().addHeader(
CONTENT_TYPE,
APPLICATION_JSON
).build()
chain.proceed(request)
}
clientBuilder.addInterceptor { chain ->
val request = chain.request()
request.newBuilder().addHeader(
ACCEPT,
APPLICATION_JSON
).build()
chain.proceed(request)
}
//Http client
val client = clientBuilder
.readTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.callTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build()
//Retrofit
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL + BASE_URP_PREFIX)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.addConverterFactory(ScalarsConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
.build()
//Init mapApiServices
apiServices = retrofit.create(ApiServices::class.java)
}
companion object {
private var apiClient: ApiClient? = null
fun getInstance(): ApiClient =
apiClient ?: synchronized(this) {
apiClient
?: ApiClient().also {
apiClient = it
}
}
}
}
How can i fix it?
The first option to add a static header is to define the header and respective value for your API method as an annotation. The header gets automatically added by Retrofit for every request using this method. The annotation can be either key-value-pair as one string or as a list of strings.
The example above shows the key-value-definition for the static header:
Further, you can pass multiple key-value-strings as a list encapsulated in curly brackets {} to the #Headers annotation.
How you can pass multiple key-value-strings as a list encapsulated in curly brackets:
A more customizable approach are dynamic headers. A dynamic header is passed like a parameter to the method. The provided parameter value gets mapped by Retrofit before executing the request.
Define dynamic headers where you might pass different values for each request:
Happy Coding!! 😎
I am learning to use retrofit, to consume Webservices, I have no problems in executing the #GET, #POST methods but now I have to execute a service where the token is sent, I really do not know how to do it, but I use POSTMAN where this field token I send from Headers in the Authorization key. I have seen other examples where OkHttpClient is used but I can not think of how to implement it.
So I execute my service with retrofit, to this same one the token in the head should be sent to him.
#GET(Constants.Retrofit.SURE_DO_YOU_LIKE_PRODUCTS)
Call<List<RelatedProducts>> getProductSureDoYouLike();
and this is my service in POSTMAN.
Like this:
#GET(Constants.Retrofit.SURE_DO_YOU_LIKE_PRODUCTS)
Call<List<RelatedProducts>> getProductSureDoYouLike(#Header("Content-Type") String contentType, #Header("Authorization") String auth);
If all requests require a Content-Type you could modify your Retrofit builder to include the header on every request:
OkHttpClient client;// = new OkHttpClient();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(5, TimeUnit.MINUTES)
.writeTimeout(5, TimeUnit.MINUTES)
.readTimeout(5, TimeUnit.MINUTES)
.addInterceptor(chain -> {
Request request = chain.request().newBuilder()
//Add this to include header in every request
.addHeader("Content-Type", "application/json").build();
return chain.proceed(request);
}).build();
client = builder.build();
retrofit = new Retrofit.Builder()
.baseUrl(NetworkConstants.BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
Then your request would be:
#GET(Constants.Retrofit.SURE_DO_YOU_LIKE_PRODUCTS)
Call<List<RelatedProducts>> getProductSureDoYouLike(#Header("Authorization") String auth);
You would then call like so:
apiService.getProductSureDoYouLike("token");
We have a case where we may have to update the public keys when using Certificate Pinning with OKHttp client and Retrofit. My question is how I would update the certificate pinner of the http client after retrofit has been initialized (like, when a new public key has been received)?
Do I update the CertificatePinner in the http client and then create a new instance of retrofit? Or is there an easier way?
Any suggestions appreciated.
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
clientBuilder.certificatePinner(NetworkUtils.getCertificatePinner()) ;
OKHTTPClient client = clientBuilder.build();
Retrofit myRetrofit = new Retrofit.Builder()
.baseUrl(url)
.client(client)
.build();
// Now I need to update the certificate pinner, like this?
client.certificatePinner(NetworkUtils.getCertificatePinner());
myRetrofit = new Retrofit.Builder()
.baseUrl(url)
.client(client)
.build();
Did you try using an interceptor. Something on the lines of inner class ExpiredSessionInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val response = chain.proceed(request)
if (response.code() == 202) {
val newRequest = request.newBuilder().build()
return chain.proceed(newRequest)
} else {
return response;
}
}
}