500 error using Skyscanner API with Kotlin and Retrofit - android

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

Related

OkHttp3 HttpLoggingInterceptor log body without logging header

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?

Sending a POST request to Google Forms returns code 400

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)

Retrofit headers interceptor does not change header

I have android app with few api calls. I noticed that every call has
#Headers("Content-Type: application/json") annotation in ApiService so I decided remove annotation and add header via interceptor to all requests:
val headers = { chain: Interceptor.Chain ->
val request = chain.request().newBuilder()
.addHeader("Content-Type", "application/json")
.build()
chain.proceed(request)
}
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BODY
val client = OkHttpClient.Builder()
.addInterceptor(headers)
.addInterceptor(logging)
.build()
val customGson = GsonBuilder()
.registerTypeAdapter(NameValuesList::class.java, NamesValuesListConverter())
.create()
val retrofit = Retrofit.Builder()
.baseUrl("http://www.$fullDomain")
.addConverterFactory(GsonConverterFactory.create(customGson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(client)
.build()
service = retrofit.create(ApiService::class.java)
But after that server return error on api call.
In logs I see that when I have explicit #Headers() annotation:
D/OkHttp: Content-Type: application/json
And after replace it with interceptor:
D/OkHttp: Content-Type: application/json; charset=UTF-8
I tried to change interceptor to this one:
val headers = { chain: Interceptor.Chain ->
val request = chain.request().newBuilder()
.headers(Headers.of(mutableMapOf("Content-Type" to "test")))
.build()
chain.proceed(request)
}
But I still see this in log:
D/OkHttp: Content-Type: application/json; charset=UTF-8
So looks like my interceptor does not apply or overridden. How to fix it?
UPD. I found the reason: when I add GsonConverterFactory it automatically add header Content-Type: application/json; charset=UTF-8. Are there any way to avoid it without implementing custom ConverterFactory?
as documentation says :
"Note: Headers do not overwrite each other. All headers with the same name will be included in the request."
refer to retrofit Headers
You can check if header exist.
val headers = { chain: Interceptor.Chain ->
val request = chain.request().newBuilder()
if(chain.request().header("Content-Type") == null){
request.addHeader("Content-Type", "application/json")
}
chain.proceed(request.build())
}
Try this
chain.request().newBuilder().removeHeader("Content-Type") .headers(Headers.of(mutableMapOf("Content-Type" to "test"))) .build()

Why is this OkHttp POST that supports a BODY missing its Content-Type Header?

I saw that Content-Type header is removed for methods that don't support a Body, but that isn't my case. I've also confirmed my User-Agent header is successfully set.
This can be done statically via the interface with the endpoint's definition but I'd favor a global Interceptor over annotating all my methods.
// Api.kt
#POST("authenticated_users")
fun postUser(
#Body newUser: NewUser
): Observable<AuthUser>
class UserRepo #Inject constructor(private val api: Api) {
fun postUser(newUser: NewUser) = api.postUser(newUser)
}
// NetModule.kt
#Provides #Singleton
fun providesOkHttpClient(cache: Cache, app: Application): OkHttpClient {
val timeoutInSeconds = 90.toLong()
val builder = OkHttpClient.Builder()
.cache(cache)
.addInterceptor(MyInterceptor(app))
.connectTimeout(timeoutInSeconds, TimeUnit.SECONDS)
.readTimeout(timeoutInSeconds, TimeUnit.SECONDS)
when {
BuildConfig.DEBUG -> {
val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.HEADERS
}
with(builder) {
addInterceptor(loggingInterceptor)
addNetworkInterceptor(StethoInterceptor())
}
}
}
return builder.build()
}
#Provides #Singleton
fun providesMoshi(): Moshi {
val jsonApiAdapterFactory = ResourceAdapterFactory.builder()
.add(TermsConditions::class.java)
.add(AuthUser::class.java)
.add(Unknown::class.java)
.build()
val builder = Moshi.Builder()
.add(jsonApiAdapterFactory)
.add(KotlinJsonAdapterFactory())
return builder.build()
}
#Provides #Singleton
fun providesRetrofit(okHttpClient: OkHttpClient, moshi: Moshi): Retrofit {
return Retrofit.Builder()
// .addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(JsonApiConverterFactory.create(moshi))
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(baseUrl)
.client(okHttpClient)
.build()
}
// MyInterceptor.kt
class MyInterceptor #Inject constructor(private val app: Application) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val initialRequest = chain.request()
val finalRequest = setHeaders(initialRequest)
return chain.proceed(finalRequest)
}
private fun setHeaders(initialRequest: Request): Request {
return initialRequest.newBuilder()
// .header("Content-Type", "application/vnd.api+json")
.header("User-Agent", "MyApp v${BuildConfig.VERSION_NAME}")
.build()
}
}
// MyViewModel.kt
fun createUser() {
userObserver = object : DisposableObserver<AuthUser>() {
override fun onNext(authUser: AuthUser) {
statusData.postValue(true)
}
override fun onError(e: Throwable) {
Timber.w(e.localizedMessage)
error.postValue(e.localizedMessage)
}
override fun onComplete() {
// no-op
}
}
userRepo.postUser(newUser)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(userObserver)
}
// log1.txt Retrofit with ScalarsConverterFactory
2018-04-18 15:20:35.772 16491-17436/com.es0329.myapp D/OkHttp: --> POST https://api.es0329.com/v5/authenticated_users
Content-Type: text/plain; charset=UTF-8
Content-Length: 259
User-Agent: MyApp v1.5.1
--> END POST
2018-04-18 15:20:36.278 16491-17436/com.es0329.myapp D/OkHttp: <-- 500 https://api.es0329.com/v5/authenticated_users (505ms)
// log2.txt Retrofit without ScalarsConverterFactory
2018-04-18 18:25:45.742 5017-6325/com.es0329.myapp D/OkHttp: --> POST https://api.es0329.com/v5/authenticated_users
Content-Type: application/json; charset=UTF-8
Content-Length: 311
User-Agent: MyApp v1.5.1
--> END POST
2018-04-18 18:25:45.868 5017-6325/com.es0329.myapp D/OkHttp: <-- 500 https://api.es0329.com/v5/authenticated_users (125ms)
// log3.txt after modifying JsonApiConverterFactory's `MediaType`
2018-04-18 20:35:47.322 19368-19931/com.es0329.myapp D/OkHttp: --> POST https://api.es0329.com/v5/authenticated_users
Content-Type: application/vnd.api+json
Content-Length: 268
User-Agent: MyApp v1.5.1
--> END POST
2018-04-18 20:35:49.058 19368-19931/com.es0329.myapp D/OkHttp: <-- 200 https://api.es0329.com/v5/authenticated_users (1735ms)
Why is it not working
Retrofit is in charge of setting appropriate content type and length based on registered converters and what you provide to your #Body parameter.
In greater detail: A Retrofit converter is responsible for transforming the type of your #Body to okhttp3.RequestBody which holds your content bytes, content length, and content type. Similarly on the way back. You supply content, ResponseBody handles details like HTTP headers.
You can't manually override these headers.
As you can see in the log, your string body gets successfully transmitted as text/plain.
--> POST https://api.es0329.com/v5/authenticated_users
Content-Type: text/plain; charset=UTF-8
Content-Length: 259
User-Agent: MyApp v1.5.1
--> END POST
That leads me to believe you have a registered converter and it's the scalar converter, which states:
A Converter which supports converting strings and both primitives and their boxed types to text/plain bodies.
What to to instead
All of the ready-made converters (Moshi, Gson, Jackson) are built to convert POJOs to application/json. This is a typical case and you should use one of these if you can. Explore source code here.
There are plenty of tutorials online for this case.
Rocky alternative
If for some reason you want/need to continue your current direction, that is prepare a JSON string manually and send that as application/vnd.api+json, you'll need a custom converter.
The aforementioned scalar converter already knows how to transform strings, so copy it into your project and adapt it to your needs (change the mime type). It's just a set of three classes:
convertor factory
request body convertor (transforms the #Body to okhttp3.RequestBody)
repsonse body convertor (transforms the okhttp3.ResponseBody to return value)

Retrofit response code 405 with message "method not allowed here"

I am using retrofit and I have a post request
interface NetworkApi {
#Headers("Content-Type: application/json")
#POST("/")
fun startLoginRequest(#Body loginModel : LoginModel) : Call<BaseResponseModel>
class Factory {
var retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
companion object{
fun getApi (): NetworkApi {
val serverApi = NetworkApi.Factory()
val api = serverApi.retrofit.create(NetworkApi::class.java)
return api
}
}
}
}
when I use this method and on response method were calling response body is always null.
fun startLoginRequest(){
val api = NetworkApi.Factory.getApi()
val request = api.startLoginRequest(loginRequestModel)
request.enqueue(object : Callback<BaseResponseModel>{
override fun onFailure(call: Call<BaseResponseModel>?, t: Throwable?) {
}
override fun onResponse(call: Call<BaseResponseModel>?, response: Response<BaseResponseModel>?) {
//here response.body is null
}
})
}
Strange thing is that, if I will send the same object using Postman everything works fine
this is httpClient interceptor log
--> POST http://myExampleHost.net/ http/1.1
Content-Type: application/json
Content-Length: 95
Authorization: auth-value
--> END POST (95-byte body)
<-- 405 Method Not Allowed http://myExampleHost.net/ (198ms)
Allow: GET, HEAD, OPTIONS, TRACE, COPY, PROPFIND, LOCK, UNLOCK
Content-Type: text/html
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
X-Powered-By-Plesk: PleskWin
Date: Thu, 17 Nov 2016 08:04:57 GMT
Content-Length: 1107
<HTML>
<HEAD>
<TITLE>405 Method Not Allowed</TITLE>
<BASE href="/error_docs/"><!--[if lte IE 6]></BASE><![endif]-->
</HEAD>
<BODY>
<H1>Method Not Allowed</H1>
The HTTP verb used to access this page is not allowed.<P>
<HR>
<ADDRESS>
Web Server at example address
</ADDRESS>
</BODY>
</HTML>
For now I'm using OkHttp RequestBuilder and everything works fine, I don't realize what I'm missing in the above example
val client = OkHttpClient()
val JSON = MediaType.parse("application/json; charset=utf-8")
val body = RequestBody.create(JSON,json)
val request1 = Request.Builder()
.url("http:example.com")
.addHeader("content-type", "application/json")
.method("POST", body)
.build()
client.newCall(request1).enqueue(object : okhttp3.Callback{
override fun onFailure(call: okhttp3.Call?, e: IOException?) {
}
override fun onResponse(call: okhttp3.Call?, response: okhttp3.Response?) {
response?.body()?.string()?.print("RETROFIT response")
}
})
My best guess is that you are trying to post to the wrong url www.example.com/login// instead of www.example.com/login/. APIs can be a bit finicky about posts.
You can add the logging interceptor so you can see what you are posting and to what URL so its a little easier to debug. To set it up you need to add compile "com.squareup.okhttp3:logging-interceptor:$okhttp_version" to your gradle file and change your retrofit setup to something like this:
val httpClient = OkHttpClient.Builder().apply {
if (BuildConfig.DEBUG) {
httpClient.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
}
}
var retrofit = Retrofit.Builder()
.client(httpClient.build())
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
That's the best advice I can give without seeing the error logs.
For me the main reason was i was calling http instead https
For Example:
i was using this url as a base url which give me this error
http://www.facebook.com/
when i changed it to this one it worked like a charm
https://www.facebook.com/
I was missing "/" at the end of my url in the POST request.
Changing url from
www.example.com/list_products
to www.example.com/list_products/ worked for me.
Method not allowed error with status code-405 is showed when we are not sending our request method or wrong request method is getting sent.
In my scenario,Api is of POST type and I was checking for GET request type,
So try change your proper Request method(GET/POST/PUT/DELETE).
I found the answer, the problem was url, my baseUrl were http//www.example.com/url1/url2/...../service
and #POST("/")
when I saw the POST endpoint it was http//www.example.com/ and I don't know why
I just changed this urls
baseUrl = http//www.example.com/
and #POST("url1/url2/...../service")

Categories

Resources