Retrofit - onFailure on non-empty body, onResponse on empty body - android

My GET request doesn't work properly in Android Studio. I tried to put the same requests in Postman and in result I always got response code 200 with correct body. However, using Retrofit I get onResponse (200 code) when response body is empty (there is no data) and onFailure when response body is not empty (there are some reservations).
EDIT: I have just noticed such error in logs: Expected BEGIN_OBJECT but was STRING at line 1 column 22 path $[0].reservationDate
EDIT2: JSON RESPONSE
[
{
"reservationDate": "2019-10-30",
"id": "4"
}
]
API Service:
#Headers("Authorization: Bearer ...")
#GET("reservations")
fun getSchedule(#Query("id") id: Int,
#Query("reservationDate") reservationDate: LocalDate
): Call<List<ScheduleModel>>
companion object {
fun create(): ScheduleService{
var retrofit = Retrofit.Builder()
.baseUrl("myUrl")
.addConverterFactory(GsonConverterFactory.create())
.build()
return retrofit.create(ScheduleService::class.java)
}
}
}
data class ScheduleModel(
val id: Int,
val reservationDate: LocalDate
)
fun getReservations() {
var service = ScheduleService.create()
var localdate = LocalDate.of(2019,10,30)
var call = service.getSchedule(4, localdate)
call.enqueue(object : Callback<List<ScheduleModel>> {
override fun onResponse(call: Call<List<ScheduleModel>>, response: Response<List<ScheduleModel>>) {
if (response.code() == 200) {
Toast.makeText(applicationContext, "It's ok", Toast.LENGTH_SHORT).show()
}
}
override fun onFailure(call: Call<List<ScheduleModel>>, t: Throwable) {
Toast.makeText(applicationContext, "Failure", Toast.LENGTH_SHORT).show()
}
})
}

Your id and reservationDate in response is a String but you try to parse it as Int and LocalDate. Change the types and it's gonna be work.

Related

How to Parse Json in Kotlin Using Retrofit?

i am new to kotlin and i am in learning phase. I have followed many links but didn't able to understand completely.
I want Json response to show in my textview.
Problem: 1
I have tried this code but was unable to get data, but i want to get the items inside data object. Quote and author are coming null.
{
"status": 200,
"message": "Success",
"data": {
"Quote": "The pain you feel today will be the strength you feel tomorrow.",
"Author": ""
},
"time": "0.14 s"
}
Problem: 2
I dont know how to parse this response in textview
object ServiceBuilder {
private val client = OkHttpClient.Builder().build()
private val retrofit = Retrofit.Builder()
.baseUrl("https://url.com.pk/") // change this IP for testing by your actual machine IP
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
fun<T> buildService(service: Class<T>): T{
return retrofit.create(service)
}}
RestApi
interface RestApi{
#Headers("Content-Type: application/json")
#POST("api/getquotes")
abstract fun addUser(#Body userData: UserInfo): Call<UserInfo>}
RestAPiService
class RestApiService
{
fun addUser(userData: UserInfo, onResult: (UserInfo?) -> Unit)
{
val retrofit = ServiceBuilder.buildService(RestApi::class.java)
retrofit.addUser(userData).enqueue(
object : Callback<UserInfo>
{
override fun onFailure(call: Call<UserInfo>, t: Throwable)
{
onResult(null)
}
override fun onResponse( call: Call<UserInfo>, response: Response<UserInfo>)
{
val addedUser = response.body()
Log.d("responsee",""+addedUser)
onResult(addedUser)
}
}
)
}
}
UserInfo
data class UserInfo (
#SerializedName("Quote")
val quote : String,
#SerializedName("Author")
val author : String
)
MainActivity
fun getQuotes() {
val apiService = RestApiService()
val userInfo = UserInfo("","")
apiService.addUser(userInfo) {
Log.d("Error registering user","errter")
/*if ( != null)
{
// it = newly added user parsed as response
// it?.id = newly added user ID
} else {
Log.d("Error registering user","errter")
}*/
}
}
Any help would be appreciated :)
Status, message and data are all part of the response so you need to take care of that. For example this
data class AddUserResponse(
val `data`: UserInfo, //like you defined it
val message: String,
val status: Int,
val time: String
)
This means parameter and response are different so the RestApi needs to be changed to this
abstract fun addUser(#Body userData: UserInfo): Call<AddUserResponse>}
This in turn also change the types in the service like
class RestApiService
{
fun addUser(userData: UserInfo, onResult: (UserInfo?) -> Unit)
{
val retrofit = ServiceBuilder.buildService(RestApi::class.java)
retrofit.addUser(userData).enqueue(
object : Callback<AddUserResponse>
{
override fun onFailure(call: Call<AddUserResponse>, t: Throwable)
{
onResult(null)
}
override fun onResponse( call: Call<AddUserResponse>, response: Response<AddUserResponse>)
{
val addedUser = response.body()
Log.d("responsee",""+addedUser)
onResult(addedUser.data)
}
}
)
}
}
now in getQuotes you will have that it is a UserInfo object
apiService.addUser(userInfo) {
val returnedUserInfo = it
}
just follow my steps :
File->settings->Plugins
search for JSON To Kotlin class and install it
again click on File->New->Kotlin Data class from JSON
paste your json code here and click on generate. It will generate POJO classes and you will good to go.
The first thing I noticed, is that the data in your json is:
"Quote": "The pain you feel today will be the strength you feel tomorrow.",
"Author": ""
While your UserInfo defined #SerializedName("message") for Quote.

how to show list from POST response using retrofit?

I want to show response POST from retrofit. I don't really know how because my response are array but with POST.
this is how my response looks like
{
"message":[
"00",
"Get Success"
],
"result":{
"listProgram":[
{
"banner":"",
"area":"",
"domainlembaga":"",
"domainprogram":"",
"donate":0
{
.
.
.
]
service
interface Service {
#POST("program/list")
fun getProgram(#Body body: Pair<String, Int>, pair: Pair<String, Int>): Call<BaseBersedekahResponse<ListProgramBersedekahResponse>>
data
data class BaseBersedekahResponse<T>(
#SerializedName("message") val message: String?,
#SerializedName("result") val result: T?
)
data class ListProgramBersedekahResponse(
#SerializedName("listProgram") val listProgram: List<ProgramBersedekahResponse>?
)
data class ProgramBersedekahResponse(
#SerializedName("banner") val banner: String?,
#SerializedName("domainlembaga") val domainlembaga: String?,
#SerializedName("domainprogram") val domainprogram: String?,
#SerializedName("donate") val donatur: Int?
)
activity
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(APIUrl.BASEURL)
.addConverterFactory(GsonConverterFactory.create())
.build()
val apiService: Service = retrofit.create(Service::class.java)
val call: Call<BaseBersedekahResponse<ListProgramBersedekahResponse>> = apiService.getProgram(
"limit" to 1,
"group" to 4)
call.enqueue(object : Callback<BaseBersedekahResponse<ListProgramBersedekahResponse>> {
override fun onResponse(call: Call<BaseBersedekahResponse<ListProgramBersedekahResponse>, response: Response<BaseBersedekahResponse<ListProgramBersedekahResponse>>) {
val getProgram: List<ProgramBersedekahResponse?>? = response.body().result.listProgram
Toast.makeText(applicationContext, "success", Toast.LENGTH_SHORT).show()
}
override fun onFailure(call: Call<BaseBersedekahResponse<ListProgramBersedekahResponse>>, t: Throwable) {
Toast.makeText(applicationContext, "Failure", Toast.LENGTH_SHORT).show()
}
})
I actually planning it to show in RecyclerView, but I still don't know how to show it the response from POST. please help.
Your signatures should match each other. You should make your signatures same as the signature in your interface.
For your request body:
data class BersedekahRequest(
#SerializedName("limint") val limint: Int,
#SerializedName("group") val group: Int
)
then your interface:
#POST("program/list")
fun getProgram(#Body body: BersedekahRequest): Call<BaseBersedekahResponse<ListProgramBersedekahResponse>>
your rest service call:
val requestBody = BersedekahRequest(10,20)
val call: Call<BaseBersedekahResponse<ListProgramBersedekahResponse>> = apiService.getProgram(requestBody)
call.enqueue(object : Callback<BaseBersedekahResponse<ListProgramBersedekahResponse>> {
override fun onResponse(call: Call<BaseBersedekahResponse<ListProgramBersedekahResponse>, response: Response<BaseBersedekahResponse<ListProgramBersedekahResponse>>) {
val GetApps2: List<ProgramBersedekahResponse?>? = response.body().result.listProgram
Toast.makeText(applicationContext, "success", Toast.LENGTH_SHORT).show()
}
override fun onFailure(call: Call<ListProgramBersedekahResponse>, t: Throwable) {
Toast.makeText(applicationContext, "Failure", Toast.LENGTH_SHORT).show()
}
})
And I think one more problem you have.
fun getProgram(#Body body: ProgramBersedekahResponse?) this #Body is the body you will send via POST request, not your response body. If you do not send anything inside post body, make it empty, or give the appropriate request body since it seems like it is your response object right now.

Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $ . How can i fix this

I'm trying to resolve the malformed JSON issue while dealing with the JSON object. It dumps the error on my project's console stating that Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $. How can I fix this?
Send Remote Message
private fun sendRemoteMessage(body: sender) {
val retrofit = Retrofit.Builder()
.baseUrl("http://fcm.googleapis.cm/fcm/")
.addConverterFactory(GsonConverterFactory.create())
.build()
retrofit.create(ApiService::class.java).sendRemoteMessage(body).enqueue( object :Callback<String> {
override fun onFailure(call: Call<String>, t: Throwable) {
Toast.makeText(applicationContext, t.message, Toast.LENGTH_LONG).show()
}
override fun onResponse(call: Call<String>, response: Response<String>) {
if (response.code() == 200 && response.isSuccessful){
Toast.makeText(applicationContext, "Message sent",Toast.LENGTH_LONG).show()
}
else {
Toast.makeText(applicationContext, response.message(), Toast.LENGTH_LONG).show()
}
}
})
}
API Service
#Headers({
"Content-Type:application/json",
"Authorization:key=myServerKey"
})
#POST("send")
Call<String> sendRemoteMessage(#Body sender body);
Handling Data
private fun initiateMeeting(meetingType: String, receiverToken: String) {
val data = user()
data.name = preferenceManager.getString(Constants().keyUserName).toString()
data.profilePic = preferenceManager.getString((Constants().keyProfile)).toString()
data.email = preferenceManager.getString(Constants().keyEmail).toString()
data.token = preferenceManager.getString(Constants().keyRemoteMsgSenderToken).toString()
val sender = sender(data, receiverToken)
sendRemoteMessage(sender)
Toast.makeText(applicationContext, sender.data.toString(), Toast.LENGTH_LONG).show()
}
Data Sender
class sender(var data: data, var to: String)
Data Class
class user: Serializable {
var name = ""
var profilePic = ""
var token: String = ""
var email = ""
}

Retrofit Kotlin unable to create a working post request

I'm trying to do a POST request using Retrofit but I'm unable to make it work. It does work on Postman. I specified the header "Content-Type: application/json" and set my "email" and "password" parameters in the body and it works well.
But it doesn't on Android. Here are my codes :
private fun login() {
val user = User("test#gmail.com", "dsea2EcFI32\\\"af'xn")
this.service.login(user).enqueue(object : Callback<LoginResponse> {
override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
if (response.code() == 200) {
// TODO
}
}
override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
// TODO
println(t.message)
}
})
}
The request :
#Headers("Content-Type: application/json")
#POST("/api/authentication/login")
fun login(#Body body: User): Call<LoginResponse>
User model
data class User(val email: String, val password: String)
LoginResponse :
class LoginResponse {
#SerializedName("user")
val user : UserResponse? = null
}
class UserResponse {
#SerializedName("id") val still : String = null
#SerializedName("firstName") val running : String = null
#SerializedName("lastName") val bicycle : String = null
#SerializedName("email") val walking : String = null
#SerializedName("token") val vehicle : String = null
}
In case the auth is a failure, the server sends me back an HTML page so the only error I have is
Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $
I already set it to true and it keeps saying me that the GSON parsed object isn't a JSON object but I know there's an Android code here
Can someone helps me finding it ?
PS : I even tried to send the body as a JSON object but same error
PS2 : might this be due to the password even If I added enough backspace to accept the special characters ? the real string is dsea2EcFI32"af'xn
EDIT :
As asked, here is my retrofit builder with the HTTPInterceptor
val client = OkHttpClient()
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
client.interceptors().add(interceptor)
val retrofit = Retrofit.Builder()
.baseUrl(BuildConfig.API_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
this.service = retrofit.create(LoginResponse::class.java)
I found the solution.
The issue was the password because it had backslashes and quotes inside of it.
Kotlin was doing a wrong parsing.
Convert your fun login object like below one.
#Headers("Content-Type: application/json")
#POST("/api/authentication/login")
fun login(#Body requestBody: RequestBody): Call<LoginResponse>
then create a fun like this
fun makeGSONRequestBody(jsonObject: Any?): RequestBody {
return RequestBody.create(MediaType.parse("multipart/form-data"), Gson().toJson(jsonObject))
}
you need to pass your User object like below
private fun login() {
val user = User("test#gmail.com", "dsea2EcFI32\\\"af'xn")
this.service.login(makeGSONRequestBody(user)).enqueue(object : Callback<LoginResponse> {
override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
if (response.code() == 200) {
// TODO
}
}
override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
// TODO
println(t.message)
}
})
}

Empty response body on retrofit call

I have a app whos call a service POST (postman test) and get some information from that call.
My retrofit initializer:
class RetrofitInitializer {
private val retrofit = Retrofit.Builder()
.baseUrl("https://bank-app-test.herokuapp.com/api/")
.addConverterFactory(GsonConverterFactory.create())
.build()
fun loginApiContract() : LoginApiContract{
return retrofit.create(LoginApiContract::class.java)
}
}
the interface:
interface LoginApiContract {
#POST("login")
fun login() : Call<UserAccount>
#GET("statements")
fun getStatements()
}
and finally the call:
val call = RetrofitInitializer().loginApiContract().login()
call.enqueue(object: Callback<UserAccount> {
override fun onResponse(call: Call<UserAccount?>?,
response: Response<UserAccount?>?) {
response?.body()?.let {
val myUserAccount: UserAccount = it
loginView.doLogin(myUserAccount)
}
}
override fun onFailure(call: Call<UserAccount?>?,
t: Throwable?) {
Log.e("onFailure error", t?.message)
}
})
I got response code 200, but response body is empty.
This is my postman response:
{
"userAccount": {
"userId": 1,
"name": "Jose da Silva Teste",
"bankAccount": "2050",
"agency": "012314564",
"balance": 3.3445
},
"error": {}
}
and this is my model:
class UserAccount constructor(var userId: Int, var name: String, var bankAccount: String, var agency: String, var balance: Double){
init{
this.userId = userId
this.name = name
this.bankAccount = bankAccount
this.agency = agency
this.balance = balance
}
}
I found!
Its a problem with my postman. For any reason my AVD cant access the mock server. now i solve my problem with a simple restful api node.js.
Thx for the help guys.
Your response contains elements named "userAccount" and "error". Your UserAccount class has neither which is causing the issue. Therefore, use Retrofit with a class like this:
data class UserResponse(val userAccount: UserAccount, val error: BackendError)

Categories

Resources