We are using retrofit to make API requests in our Android app and are running into a weird issue. I have a request that looks like this:
interface apiservice {
@POST("user/login")
fun postLogin(@Body body: LoginBody): LiveData<ApiResponseWrapper<LoginRemote>>
I make the request by doing:
apiservice.postLogin(
....
)
However, the first time this get's submitted, the request url is:
http://localhost/
First off, there isn't a single configuration in my app that sets the base url as localhost. Secondly, this request doesn't even have the /user/login appended to it.
This was working fine the last time we worked on this app a few weeks ago, but now it has this weird bug when we didn't even touch anything related to this...
EDIT
Here is my Retrofit builder
Retrofit.Builder()
.baseUrl("${BuildConfig.API_URL}/en/api/v1/")
.client(makeOkHttpClient(BuildConfig.DEBUG, sessionData))
.addCallAdapterFactory(LiveDataCallAdapterFactory())
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
.create(YTPService::class.java)
Here is my Build Config in my :
buildConfigField("String", "API_URL", "\"https://myurl.org\"")
// BuildConfig's `DEBUG` gets set to FALSE when build occurs
And then this is what is printing out in the log files during this process:
// THE FIRST TIME I HIT SUBMIT AND TRIGGER THE REQUEST
D/LOGIN-RESPONSE: Response{protocol=http/1.1, code=500, message=Response.error(), url=http://localhost/}
Error(errors=[com.mycompany.myproject.api.auth.login.LoginResponse$ErrorType$Generic@2148520])
...
// THE SECOND TIME I HIT SUBMIT AND TRIGGER THE REQUEST IMMEDIATELY AFTER FIRST FAILED
D/LOGIN-RESPONSE: Response{protocol=http/1.1, code=200, message=Logged in as SampleUser1., url=https://myurl.org/en/api/v1/user/login}
Ok(data=com.mycompany.myproject.api.auth.login.LoginResponse$SuccessData@4f03c75)
For even more details, check out this issue I opened on their repo
UPDATE
After looking at the logs on the server, I have confirmed that the proper request is actually being made, but retrofit's ApiResponseWrapper isn't setting the response with the proper information sent back from the server.
e.g.
apiservice.postLogin(LoginBody(username, password)).map { // it: ApiResponseWrapper<LoginRemote>
// `it.response` == Response{protocol=http/1.1, code=500, message=Response.error(), url=http://localhost/}
// When, in fact, the server actually sent back a 200 with the data I need. Retrofit just isn't handling it correct...
}
EDIT 2
Adding ApiResponseWrapper and LoginRemote classes as requested
ApiResponseWrapper
sealed class ApiResponseWrapper<R> {
companion object {
fun <R> success(response: Response<R>): ApiResponseWrapper.Success<R> =
ApiResponseWrapper.Success(response)
fun <T> failure(error: Throwable): ApiResponseWrapper.Failure<T> =
ApiResponseWrapper.Failure(error)
}
class Failure<T>(val error: Throwable) : ApiResponseWrapper<T>()
class Success<T>(val response: Response<T>) : ApiResponseWrapper<T>()
}
LoginRemote
class LoginRemote(@SerializedName("user") val user: User) {
companion object {
fun parseResponse(response: Response<LoginRemote>): LoginResponse {
return if (response.isSuccessful) {
Log.d("LOGIN-RESPONSE-BODY", response.body().toString())
response.body()!!.format()
} else if (response.code() == 302) {
// accept terms again
LoginResponse(listOf(LoginResponse.ErrorType.TermsRequired()))
} else {
val errorBody = response.errorBody()?.string()?.trim() ?: ""
if (errorBody == "[\"Wrong username or password.\"]" || errorBody.contains("has not been activated or is blocked."))
return LoginResponse(listOf(LoginResponse.ErrorType.CredentialsInvalid()))
if (errorBody.contains("[\"Already logged in as "))
return LoginResponse(LoginResponse.SuccessData(null, null, null))
return LoginResponse(listOf(LoginResponse.ErrorType.Generic()))
}
}
}
fun format(): LoginResponse {
Log.d("LOGIN-ROLE-KEYS", user.roles.toString())
val roles = user.roles.keys
val role = when {
roles.contains(ROLE_ID_STANDARD) -> Role(ROLE_ID_STANDARD)
roles.contains(ROLE_ID_LIMITED) -> Role(ROLE_ID_LIMITED)
else -> Role(ROLE_ID_PREMIUM)
}
val countryOfResidence = if (user.countryOfResidence != null) user.countryOfResidence!!.und[0].value else "United Arab Emirates"
return LoginResponse(LoginResponse.SuccessData(role = role, gender = user.fieldGender, countryOfResidence = countryOfResidence))
}
data class User(
@SerializedName("field_gender") val fieldGender: Gender?,
@SerializedName("roles") val roles: Map<Int, String>,
@SerializedName("field_new_residence") val countryOfResidence: CountryOfResidence?
)
data class CountryOfResidence(
@SerializedName("und") val und: List<CountryOfResidenceUND>
)
data class CountryOfResidenceUND(
@SerializedName("value") val value: String
)
}
from Android Retrofit attempting to make incorrect request on first attempt
No comments:
Post a Comment