I'm building an Android app, which uses Retrofit for the networking.
Here's the code:
private val loggingInterceptor by lazy {
HttpLoggingInterceptor(HttpLoggingInterceptor.Logger { message -> Log.d(TAG, message) }).apply {
level = if(BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
}
}
val client = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build()
Retrofit.Builder()
.baseUrl(" https://mybaseurl.com/")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
I use an HttpLoggingInterceptor, however logs are too verbose because it logs all the headers..
content-language: en-GB
content-type: application/json
date: Sun, 07 Jul 2019 12:52:36 GMT
p3p: CP="NON CUR OTPi OUR NOR UNI"
transfer-encoding: chunked
cache-control: no-cache,no-store,must-reva
x-powered-by: Servlet/3.0
expires: Thu, 01 Dec 1994 16:00:00 GMT
pragma: no-cache
and more....
If I switch to LogLevel Basic though, I don't get the request and response, but just the URLs.
Btw, looking inside HttpLoggingInterceptor code:
boolean logHeaders = logBody || level == Level.HEADERS;
So it seems that there is no actual way to log body without the headers.
Is there any hacky way or workaround?
Related
I want to connect to the Skyscanner API, using Kotlin and Retrofit. https://rapidapi.com/skyscanner/api/skyscanner-flight-search
When attempting to POST the 'create session' call, I get a 500 error, but the logs aren't giving a specific reason. I can only assume that my post data isn't being formatted correctly, but I'm using Retrofit with GSon to handle this for me.
One clue, is that in their Java sample code, they pass the form data in the following format: "inboundDate=2019-09-10&children=0&adults=1" whereas after GSon convertion from my sessionObject class, my data is in the format {"adults":1,"country":"GB","outboundDate":"2020-01-06"} - I'm unsure how, using Retrofit, I can pass my data in that format, and whether that's the issue causing the 500.
Here are some code snippets:
// my object for posting data
class SessionBody {
#SerializedName("country")
var country: String = ""
#SerializedName("currency")
var currency: String = ""
...etc...
// my interface
#Headers("Content-Type: application/x-www-form-urlencoded")
#POST("pricing/v1.0/")
fun postUser(#Body sessionBody: SessionBody): Call<Void>
// my connector class
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val client = OkHttpClient.Builder()
.addInterceptor(Interceptor { chain ->
val original = chain.request()
val request = original.newBuilder()
.header("X-RapidAPI-Host", "skyscanner-skyscanner-flight-search- v1.p.rapidapi.com")
.header("X-RapidAPI-Key", "...my key here...")
.method(original.method(), original.body())
.build()
return#Interceptor chain.proceed(request)
})
.addInterceptor(interceptor)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
.client(client)
.build()
val api = retrofit.create(TravelEzyApi::class.java)
val call = api.postUser(sessionBody)
And here is the output from the logs...
D/OkHttp: --> POST https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com/apiservices/pricing/v1.0/
D/OkHttp: Content-Type: application/x-www-form-urlencoded
Content-Length: 142
X-RapidAPI-Host: skyscanner-skyscanner-flight-search-v1.p.rapidapi.com
X-RapidAPI-Key: ... my key here ...
{"adults":1,"country":"GB","currency":"GBP","destinationPlace":"BKK-sky","locale":"en-GB","originPlace":"LHR-sky","outboundDate":"2020-01-06"}
--> END POST (142-byte body)
D/OkHttp: <-- 500 Internal Server Error
D/OkHttp: Cache-Control: private
Content-Type: application/json
Date: Tue, 22 Oct 2019 10:48:44 GMT
Server: RapidAPI-1.0.32
X-RapidAPI-Region: AWS - eu-west-1
X-RapidAPI-Version: 1.0.32
Content-Length: 2
Connection: keep-alive
{}
D/OkHttp: <-- END HTTP (2-byte body)
Any help or clues greatly appreciated.
The 500 Status code indicates that the server has encountered a situation it does not know how to handle.
This should work fine by now. You can always write to the RapidAPI support team if the error still persists at support#rapidapi.com
In my android app, I tried to send a POST request to Google Forms, but an error 400 is returned:
400 https://docs.google.com/forms/d/e/[form-id-number-here]/formResponse (114002ms)
content-type: text/html; charset=utf-8
x-robots-tag: noindex, nofollow, nosnippet
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: Mon, 01 Jan 1990 00:00:00 GMT
date: Sun, 17 Mar 2019 05:03:05 GMT
p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info."
p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info."
x-chromium-appcache-fallback-override: disallow-fallback
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
server: GSE
....
Here is my code:
interface FormApi {
#Headers("Content-Type: application/json", "Accept: text/html", "Cache-Control: no-cache")
#POST("{key}/formResponse")
fun formResponseJson(#Path("key") key: String, #Body json: JsonObject): Call<ResponseBody>
}
Usage:
val gson = GsonBuilder().setLenient().create()
val client = OkHttpClient.Builder()
.readTimeout(timeout.toLong(), TimeUnit.SECONDS)
.connectTimeout(timeout.toLong(), TimeUnit.SECONDS)
.build()
val retrofit = Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create(gson))
.client(client)
.build()
val api = retrofit.create(FormApi::class.java)
val json = dataObj.toJson()
val call = api.formResponseJson(key, json)
call.enqueue(CallBackResult(resultReceiver))
Note that the json object has the key-value format like: {"entry.xxxxx1":"Test"}
Does anyone have an idea why this error code is returned? Has anyone successfully sent a POST to a Google Form in Android?
Thanks.
I have made the following modifications, and the code is now able to post to Google forms:
In the FormApi:
POST("{key}/formResponse")
#FormUrlEncoded
fun formResponse(#Path("key") key: String, #FieldMap hashmap: HashMap<String, Any>): Call<ResponseBody>
Usage:
val call = api.formResponse(key, hash)
where "hash" is a HashMap with the actual values of the fields (not URLEncoded)
I have been using retrofit and auth for a while with no problem.
But from this API call while everything is exactly the same as my other projects which works, I'm constantly getting error code 401
here is my API calling code:
#GET("workers")
fun getWorkersList(): Observable<BaseResponseModel<WorkerAttributes>>
companion object Factory {
fun create(): WebServices {
val okHttpClientBuilder = OkHttpClient.Builder()
val httpLoggingInterceptor = HttpLoggingInterceptor()
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
okHttpClientBuilder.addInterceptor(httpLoggingInterceptor)
okHttpClientBuilder.addInterceptor { chain ->
val request = chain.request().newBuilder()
request.addHeader("Authorization", "Bearer eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiUlNBLU9BRVAiLCJraWQiOiIzaGltWWEyYmJHS18zSVdZMUxUUHdyTUl2ZkF3ZVhZQmRNLTU5XzN6c0MwIn0.DIwk0Q215_M7rt3iGfyRVrImhKCXvEB5NBoLQj6iqBnN9yCWWBhGW9fmX6beOajtWgC8gpupe1hJ-iurFRbo2Eg52Nh9ljkkO5Mz8ulSpH7YlfP8Yi-TBrNoPIf_IvHrr1Rt8bG0w2Ie1Jo3BBOguMQ6Q2WI4fQDY-oFIluxOQ5t9E0_XvDhna7maiQnWevLbu80Mj7g643v5nvNWOdpSAXW91rZXIXdal_A_ffY-IUyvYhEJuOkyHWwNonuGjiGHWKPTjTR16w3seoSs4srkacOIN1cmMdXKlkvlWSYCv5Nd8xJ_rTqA4GIyKLRSALYcuiTFMOIxeMIpPB5DjpMTOmOD72QnD8MHMx-SNHk2Yva5iPs__h_WXIKzjME6wRQxTBayfWcXuA6loTdSXnV5Tfl63y62nYZUesh2ElcF5ZK_if3R70jTh-_dxAc8-EcxagAgeCAYNGx-i0fokA4Crfz0A9MKJv5XKn8hY-_4_AT-tsIwBQ6YWYISPBGqyUFoBX0LGDEojIywQBXcLSqHRoah45IDW87TTjVTtPjKsxrIr9rl661dYKRE4ViTLGON0hRBmmNAOokcTNv4tEPBWDS5OPIVtnqkCruAV1YXGKMWpbGOlH6pi2sLY5FtNP9h7nNKnK59gX_FMwXyLjzdPU7HkEEv2svTHN_vBFDUeE.UmwA_rn0FGSRXl4kVPrF3Q.lpZ6JQSke2wLCi3X18mNMAYs5x2nCk7LGrs65nOo-cv8FQ1zfYz9qj7TdH26BxmIrtJiBlLkLhJJnWmSjceNsQnCaOFt36cStJKTHbRkydezuBJyIgvlQH-8Y3nTXLpIbo3JQjxTJ-lUwmY8bLnyxS91DaKEjmip3DoXJLSa4uOivuC68npCCtPVFeHbdHrzS_wQTRsUEh1o0XtWI1TtW868mV3njXwtjHdKxp7Uk8wfY5i5R4qJHxXWgf8PuXJA99Hh1Jwzee_tuA67wnPxvKf95mFkZVYZdDqNKrFkDE9B7-r5FjLi3pQZdTMXjFBGAMv20jrM5BuvCZLP9p6ugbG5pOBRUjBT5RtQpw48_th6pTa4W36MkBkh5TbXS27yuOXvi-b3blcSCEiizEDcJvqm4DBwqCNlSR8iG_QgktFDzkG99--LHV4R956K6cAozVAeyv0Jo9mKfaTsRLlMNhfTFpPrZmR5hVb11CEmmLi9C84aK1eivlYOZenzdZKpYE2mBKjZXOl96PJ3jb-OwEO2MDw9fyGzez8NaVhWAGGw9rm1iXnQyGljOw_ufOr5GLPHXXMhjSGVpXbN65sCTv8p6XGZnJiW7otVUzzmxG_kYQ3tkFLTyG9p6SG3GGBDVYG_pkDpPWavWlQdplIDTsOrjoXu-8sAYTx6GO_rGL13haW7KY9pRi9v3324zkNYOYL8CWWZzjcm26607lTEhpVWvkP4QwisGjvrAMoyj-GnlKvyK1u-1sPcuRMUg-8AKRYzBdtlChoUOtdVwYsOHxiLAkRMBH4PRIqWyUNILmlugXGNA7umZxj8uWy2mKjpZ7M8D_YlR66tj4duARp0lJYfeuvDqbXABVuSoLs8qaEFuYXLfiTNH4zItNeMQjfzT_eh9S_ligNALdOb82FgZzYv5ZKbFMo2s9kwzAb4PdwyaZjIP4_UJiJBqOazslWJYpdMLY2ZHGwfg0ZFP3adV06hbR1i9bJ7lyew2Dalqh9Sq-cFPg32-6rbZXYJnFFzfMks4FOJCgPOhCz2QODdbZU8_nQp_EQ4mIXnD2BR6BcCa3odD8rliM3IYOWnq3AhCHen55FwygCw6-u61Q03m4httHFozE-x4YiQivTQ70YUmdYNMyU_chQ3WfiJzaOgEwjq8vIP0hasxiaE1fBG7PncjGCUNB9sFiyVYjmSbP68iIQFqQ10oo9hnMYVxs_nSdem23fDNOutoikzjSHPo4_qxg-hNV0GDIrzBPLGsGw_W9agnUQOpwtpYycZ9v472FWhI5z0c8-8U7D9cV9s0ELyO9U7_vHhSoZJCq1edIFVVKTHsVfR2-vOlCHuqlbH4TzqGtIzf-5nUuY7HQ4iIxdjOrfU2GtMaU_tB8v3ZsC2Nr7IMYeyeBhItOXccNnqHUhMyZ-LdHkoafendmWTnnMOlkK2l91Jil4tPrJyVn1YFlsbzobQOurrsz2MAW3Ew1ibTCsRvlKWRs8dOSAxoUBcQL-r9g5_BcF0wsV7xzWvIHFm7fOuqLGsjsrCMAuXKImA2tFYECpr6vqCq8ORdqapUOlMb0_K4Iae-AhEkbMVERgLS3O05tRhQATMl2jnqA71DImzDoQMC_c45ELXToqX7x0oiINsOLQKBFQjiQ4-G9C1DJlZ67ZVtSN2PyKVNqMNegEtSz1uieOmkoXa1Vq62CrDp7KORToTHMZEV9uWe7KAOzqYm7jwhEvFs2jgvSga9Au2YgzqxbBjpC8RDSQqhUZTQeg2gecFLRpIObQ._YXzEw0gT4HbLqcFfdWoFQ")
request.addHeader("Content-Type", "application/json")
request.addHeader("X-Requested-With", "XMLHttpRequest")
request.addHeader("Cookie", "XMLHttpRequest")
chain.proceed(request.build())
}
val retrofit = Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClientBuilder.build())
.baseUrl(Constants.BASE_URL)
.build()
return retrofit.create(
WebServices::
class.java
)
}
}
and here is my error:
D/OkHttp: --> GET http://pilot.wakecap.com/api/sites/10010001/workers
D/OkHttp: --> END GET
I/art: Do partial code cache collection, code=60KB, data=57KB
I/art: After code cache collection, code=58KB, data=56KB
I/art: Increasing code cache capacity to 256KB
D/OkHttp: <-- 401 Unauthorized https://pilot.wakecap.com/api/sites/10010001/workers (2645ms)
D/OkHttp: Server: nginx/1.14.0 (Ubuntu)
D/OkHttp: Date: Wed, 16 Jan 2019 20:16:44 GMT
D/OkHttp: Content-Type: application/json; charset=utf-8
D/OkHttp: Content-Length: 83
D/OkHttp: Connection: keep-alive
D/OkHttp: X-DNS-Prefetch-Control: off
D/OkHttp: X-Frame-Options: SAMEORIGIN
D/OkHttp: Strict-Transport-Security: max-age=15552000; includeSubDomains
D/OkHttp: X-Download-Options: noopen
D/OkHttp: X-Content-Type-Options: nosniff
D/OkHttp: X-XSS-Protection: 1; mode=block
D/OkHttp: Access-Control-Allow-Origin: *
D/OkHttp: ETag: W/"53-ocmggiYI6s3nDDtb9FHiqlrJJYU"
D/OkHttp: {"code":"unauthenticated","message":"Server Error. Try again later!!","status":401}
D/OkHttp: <-- END HTTP (83-byte body)
PS: I have also used Cookies but it didn't help
PS2: it says try again later, but it works perfectly fine with Postman
PS3: For some reasons, Volley works just fine :|
So I've found out what was the problem.
The problem was I had to use "https" and I was using http, apparently Volley and Postman both automatically add the s in the end but Retrofit didn't for some reasons.
I am trying to download a file from server using retrofit. Using HttpLoggingInterceptor I tried logging what is happening. I can find the file name. But the response body is empty.
I am new to using retrofit. can some one point me out in the right direction on where I am going wrong
Retrofit Client Interface:
public interface DownloadTransactions {
#Streaming
#GET("common/frontend/member/download.aspx")
Call<Void> getBalancesAndTransactions(#Header("Cookie") String header);
}
Java call:
void downloadData() {
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS);
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
client.addInterceptor(logging);
}
Retrofit.Builder builder = new Retrofit.Builder();
Retrofit retrofit = builder.client(client.build())
.baseUrl("https://" + getString(R.string.root_url))
.addConverterFactory(ScalarsConverterFactory.create())
.build();
DownloadTransactions downloadTransactions = retrofit.create(DownloadTransactions.class);
Call<Void> call = downloadTransactions.getBalancesAndTransactions(CommsManager.getInstance().getASPSessionId());
call.enqueue(new Callback<Void>() {
#Override
public void onResponse(Call<Void> call, retrofit2.Response<Void> response) {
Log.v(TAG, "success transactions: --------" + response);
}
#Override
public void onFailure(Call<Void> call, Throwable t) {
Log.v(TAG, "failure transactions: --------");
}
});
}
Response in log:
<-- 200 OK https://xxxxx.com/common/xxx/member/download.aspx (387ms)
D/OkHttp: Cache-Control: private
D/OkHttp: Content-Type: text/csv; charset=utf-16
D/OkHttp: Server: Microsoft-IIS/8.5
D/OkHttp: Content-Disposition: attachment; filename=Account_Summary_8978_04062018_142921.csv
D/OkHttp: X-AspNet-Version: 4.0.30319
D/OkHttp: X-Powered-By: ASP.NET
D/OkHttp: Date: Mon, 04 Jun 2018 13:29:21 GMT
D/OkHttp: Connection: keep-alive
D/OkHttp: Content-Length: 446900
D/OkHttp: Set-Cookie: xxx; path=/; Httponly; Secure
D/OkHttp: <-- END HTTP (binary 446900-byte body omitted)
D/Accounts Overview: success transactions: --------
Response{protocol=http/1.1, code=200, message=OK, url=xxx}
But the response body is empty.
Retrofit determines the type of response with the return type of the methods defined in your API interface.
The return type of getBalancesAndTransactionsis Void which means nothing.
Solution : Define your customise POJO class and replace Void with `POJO
or Just for testing purpose, you can use Object which can handle any type of response but you will have to use casting for specific operation
e.g
#Streaming
#GET("common/frontend/member/download.aspx")
Call<Object> getBalancesAndTransactions(#Header("Cookie") String header);
References :
How can we handle different response type with Retrofit 2?
Using Retrofit in Android
I use retrofit and picasso library. Picasso manages caching itself. But when I view logcat, I see log below. What does it mean? Doesn't the webservice and backend send cache info correctly? How can I solve this to make Retrofit cache correctly. So, Does this data make sense?
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Content-Type: application/json
Date: Wed, 14 Dec 2016 07:15:48 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
OkHttp-Received-Millis: 1481597410805
OkHttp-Response-Source: NETWORK 200
OkHttp-Selected-Protocol: http/1.1
OkHttp-Sent-Millis: 1481597409021
Pragma: no-cache
Server: Apache
Transfer-Encoding: chunked
You can set cache option to OkHttp with using Interceptor.
OkHttpClient client = new OkHttpClient();
client.networkInterceptors().add(new CachingControlInterceptor());
Retrofit restAdapter = new Retrofit.Builder()
.client(client)
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
and CachingControlInterceptor is:
public class CachingControlInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// Add Cache Control only for GET methods
if (request.method().equals("GET")) {
if (ConnectivityUtil.checkConnectivity(YaootaApplication.getContext())) {
// 1 day
request = request.newBuilder()
.header("Cache-Control", "only-if-cached")
.build();
} else {
// 4 weeks stale
request = request.newBuilder()
.header("Cache-Control", "public, max-stale=2419200")
.build();
}
}
Response originalResponse = chain.proceed(request);
return originalResponse.newBuilder()
.header("Cache-Control", "max-age=600")
.build();
}
}
See this:
https://stackoverflow.com/a/34401686/850347