Unable to get error response in error body of retrofit - android

I am unable to get 400 response in error body of retrofit. I have set logging level its showing in logs but not showing in error body i have searched a lot but didn't find any solution is anyone there who help me in this case to get rid of this problem
call_.enqueue(object : Callback<ResponseBody> {
override fun onResponse(call: Call<ResponseBody>?, response: Response<ResponseBody>?) {
if (response?.code() == 400) {
var jObjError: JSONObject? = null
try {
var jObjErrorr = response.errorBody().string()
CustomLogs.displayLogs("$TAG jObjErrorr: $jObjErrorr")
} catch (e: Exception) {
}
try {
val string = jObjError?.getstring("error_description")
CustomLogs.displayLogs("$TAG jObjError: $string")
} catch (e: Exception) {
e.printStackTrace();
}
}
}
i need error body to get and display message and my log shows this
{"error":"Authorize","error_description":"Error in authentication"}
but error body is not showing this object

As IntelliJ Amiya mentioned in comment to your original post you should do this in onFailure method. As far as I know Retrofit's onResponse will not be called in cases of response code not in 200 range (200, 201, 202 etc.) so your check for if (response?.code() == 400) will never return true.

If you go through the Retrofit onResponse Library...,it's clearly mentioned that Retrofit does not create Body for response with status code below 200 or above 300.You have to specify your error Response Specifically!!

Decided to add it as separate answer:
if (response?.code() == 400) {
var jObjError: JSONObject? = null
try {
jObjError = response.errorBody().string()
CustomLogs.displayLogs("$TAG jObjError: $jObjError")
} catch (e: Exception) {
}
try {
val string = jObjError?.optString("error_description")
CustomLogs.displayLogs("$TAG jObjError: $string")
} catch (e: Exception) {
e.printStackTrace();
}
}
Could you try this fragment?

you can do this in Kotlin:
val errorResponse: ErrorMessage? = Gson().fromJson(
response.errorBody()!!.charStream(),
object : TypeToken<ErrorMessage>() {}.type
)

Related

Retain errorBody from HttpException

I am trying to map the error body from an exception into into a useful model with now luck, using moshi and retrofit 2.9.0
I found numerous posts discussing the same issue but none of the solutions worked for me.
My code is the following
private fun getErrorMessageFromGenericResponse(httpException: HttpException): String? {
var errorMessage: String? = null
try {
val body = httpException.response()!!.errorBody()!!
val errorResponse = body.string().toObject(ErrorResponse::class.java)
errorMessage = errorResponse?.message
} catch (e: IOException) {
e.printStackTrace()
} finally {
return errorMessage
}
}
fun <T> String.toObject(objectClass: Class<T>): T? {
val moshi = Moshi.Builder().build()
val adapter = moshi.adapter(objectClass).lenient()
return adapter.fromJson(this)
}
I tried also using this but it also does not work:
httpException.response()!!.errorBody()!!.source().buffer.snapshot().utf8()
I am probably missing something really simple as I think its a common usecase.
Thanks in advance.
fun handleErrorResponse(e: Throwable): String {
return when (e) {
is HttpException -> {
parseHTTPError(e.response()!!.errorBody())
}
is SocketTimeoutException -> {
ApiConstants.TIME_OUT
}
is IOException -> {
ApiConstants.SERVERERROR
}
else -> ApiConstants.SERVERERROR
}
}
fun parseHTTPError(responseBody: ResponseBody?): String {
try {
val jsonObject=JSONObject(responseBody!!.string())
try {
val error=jsonObject.getJSONArray("message")
return error[0].toString()
}
catch (ex: Exception) {
responseBody!!.close()
return ""
}
responseBody.close()
return ""
}

Android studio kotlin function throw exeption

Im developing an app using kotlin and MVVM architecture.I have the three layers activity,viewModel and repository, in repository i have renameDirectory() function it does some network calling to rename a directory, the function can throw an exception if the network response returns an error the problem is that the catch block in the activity layer does not catch the exception.
renameDirectory in repository
suspend fun renameDirectory(token : String,directory: Directory) {
val resp = maApi.renameDirectory("jwt $token",directory)
if(resp.isSuccessful)
return
val gson = Gson()
val type = object : TypeToken<ErrorResponse>() {}.type
val errorResponse =
gson.fromJson<ErrorResponse>(resp.errorBody()!!.charStream(), type)
throw Exception(errorResponse.error)
}
code in viewModel that calls the function
suspend fun renameDirectory(directory: Directory){
viewModelScope.launch (Dispatchers.IO){
directoriesRepository.renameDirectory(userResp!!.token!!,directory)
}
}
code in activity to calls the function and handle exceptions
try {
viewModel.renameDirectory(directory)
withContext(Dispatchers.Main) {
horizontalProgress.toggle()
activityView.snackBar("Directory has been renamed successfully")
currentFragment.clearSelection()
}
} catch (ex: IOException) {
Log.d("IO Exception=>", ex.toString())
} catch (ex: HttpException) {
Log.d("Http Exception=>", ex.message())
} catch (ex: Exception) {
this.cancel()
withContext(Dispatchers.Main) {
horizontalProgress.toggle()
activityView.snackBar(ex.message!!)
}
}
when renameDirectory in repository calls throw Exception() the app stops,why the code in activity does not handle the exception?

How can I try catch a val in fetch data successfully?

I am trying to fetch a JSONObject "image" that is sometimes a null value, so I set a try {} catch {} for my val jsonMedia in order to make it nullable but Im not sure if I'm missing something because it doesnt seem to work.
Whenever I comment out the jsonMedia my fetch works fine so I'll focus on just sharing the fetch of the "image" so the data I share doesnt get confusing.
1st. Ill show how the "image" is in the database and what I mean by "sometimes" has a null value
- null:
{
"listCar": [
{
//other data
"image": null,
//other data
}
]
}
when it contains an image:
{
"listCar": [
{
//other data
"image": {
"path": "Upload/UploadCarMain/UploadCarMain-200-200/30032235992008220498istanbul_adalarCar195.jpg",
"name": "30032235992008220498istanbul_adalarCar195.jpg"
},
//other data
}
]
}
Now my fetch data was followed by a course I'm watching the only difference is the values of "image" in the database Im using arent like the course which did not contain null values in their image path
Now here is my "Car" class that is supposed to get the image:
class Car(other data, val image: String?, other data) {
override fun toString(): String {
return "Car(//other data, image='$image', //other data')"
}
}
and here is my fetch code where I am trying to try {} catch(){} my "jsonMedia" image JSONOBject:
override fun doInBackground(vararg params: String?): ArrayList<Car> {
Log.d(TAG, "doInBackground starts")
val carList = ArrayList<Car>()
try {
val jsonData = JSONObject(params[0])
val itemsArray = jsonData.getJSONArray("listCar")
for (i in 0 until itemsArray.length()) {
val jsonCar = itemsArray.getJSONObject(i)
//other fetch data
val jsonMedia: JSONObject? = try {jsonCar.getJSONObject("image")} catch (e: IllegalArgumentException) {null}
val photoURL = jsonMedia?.getString("path")
val carObject = Car(other data, photoURL)
carList.add(carObject)
Log.d(TAG, ".doInBackground $carObject")
}
} catch (e: JSONException) {
e.printStackTrace()
Log.e(TAG, "doInBackground: Error processing json data. ${e.message}")
cancel(true)
listener.onError(e)
}
Log.d(TAG, ".doInBackground ends")
return carList
}
override fun onPostExecute(result: ArrayList<Car>) {
Log.d(TAG, "onPostExecute starts")
super.onPostExecute(result)
listener.onDataAvailable(result)
Log.d(TAG, ".onPostExecute ends")
}
}
and here is the error I get when i run the code:
2020-02-14 16:42:16.483 26642-26684/com.example.wasit E/GetFlickrJsonData: doInBackground: Error processing json data. Value null at image of type org.json.JSONObject$1 cannot be converted to JSONObject
Just modify the code like this. Exception class is the Parent class of all exception and provide exact cause of error.
for (i in 0 until itemsArray.length()) {
try {
val jsonCar = itemsArray.getJSONObject(i)
//other fetch data
val jsonMedia: JSONObject? = jsonCar.getJSONObject("image")
val photoURL = jsonMedia?.getString("path")
val carObject = Car(other data, photoURL)
carList.add(carObject)
} catch (ex: Exception) {
Log.d(TAG, ex.localizedMessage.toString())
}
}
I found a way.
I had to wrap my "jsonMedia" in the following try catch:
val jsonMedia: JSONObject? = try {jsonCar.getJSONObject("image")} catch (e: JSONException) {
null
}
Now my images that are null contain placeholders and the others work fine!

Can`t solve "Content-Length and stream length disagree" error

I get an error while trying to execute the following code:
fun parseErrorCodes(response: Response<*>): List<String> {
val errorCodes: MutableList<String> = ArrayList()
try {
val listType = object : TypeToken<ArrayList<ApiError>>() {
}.type
val errorJson = JsonParser().parse(response.errorBody().string()).asJsonObject.get("response")
if (errorJson.isJsonArray) {
val errors = Gson().fromJson<List<ApiError>>(errorJson, listType)
for (apiError in errors) {
errorCodes.add(apiError.errorCode)
}
return errorCodes
} else {
errorCodes.add(Gson().fromJson(errorJson, ApiError::class.java).errorCode)
return errorCodes
}
} catch (e: Exception) {
e.printStackTrace()
}
return errorCodes
}
The error occurs at the line : val errorJson = JsonParser().parse(response.errorBody().string()).asJsonObject.get("response")
Can someone help me to solve this error?
I found the answer to my question. The problem was that I was trying to parse the response for the API twice, first time to show the error messages and then to get the error codes to handle them for future validations.
This is how my code looks:
ErrorHandler.showError(activity, response)
val errorCodes = ErrorHandler.parseErrorCodes(response)
handleErrorCodes(errorCodes)
So, both methods showError and parseErrorCodes were working with the API response.

How to handle error in Retrofit 2.0

I want to handle error in Retrofit 2.0
Got e.g. code=404 and body=null, but errorBody() contains data in ErrorModel (Boolean status and String info).
This is errorBody().content: [text=\n{"status":false,"info":"Provided email doesn't exist."}].
How can I get this data?
Thank for helping me!
This is my code for Retrofit request:
ResetPasswordApi.Factory.getInstance().resetPassword(loginEditText.getText().toString())
.enqueue(new Callback<StatusInfoModel>() {
#Override
public void onResponse(Call<StatusInfoModel> call, Response<StatusInfoModel> response) {
if (response.isSuccessful()) {
showToast(getApplicationContext(), getString(R.string.new_password_sent));
} else {
showToast(getApplicationContext(), getString(R.string.email_not_exist));
}
}
#Override
public void onFailure(Call<StatusInfoModel> call, Throwable t) {
showToast(getApplicationContext(), "Something went wrong...");
}
});
If you want to get data when error response comes (typically a response code except 200) you can do it like that in your onResponse() method:
if (response.code() == 404) {
Gson gson = new GsonBuilder().create();
YourErrorPojo pojo = new YourErrorPojo();
try {
pojo = gson.fromJson(response.errorBody().string(), YourErrorPojo.class);
Toast.makeText(context, pojo.getInfo(), Toast.LENGTH_LONG).show();
} catch (IOException e) {
// handle failure at error parse
}
}
When generating YourErrorPojo.class do following steps :
Go to Json Schema 2 Pojo
Paste your example Json, and select source type Json , annotation Gson
Your example Json is : {"status":false,"info":"Provided email doesn't exist."}
Click Preview and it will generate your Pojo class for you.
Add this to your build.gradle : compile 'com.google.code.gson:gson:2.7'
I used Gson in this solution but you can get your Json string using: response.errorBody().string()
Retrofit doesn't see 404 as a failure, so it will enter the onSuccess.
response.isSuccessful() is true if the response code is in the range of 200-300, so it will enter the else there.
if (response.isSuccessful()) {
showToast(getApplicationContext(), getString(R.string.new_password_sent));
} else {
// A 404 will go here
showToast(getApplicationContext(), getString(R.string.email_not_exist));
}
However since your response was not successful, you do not get the response body with .body(), but with errorBody(), errorBody will filled when the request was a success, but response.isSuccessful() returns false (so in case of a status code that is not 200-300).
I'm using this library Retrobomb, you don't have to serialize at that level.
it's easy to use and customize. It supports annotation for each error type or error code.
If you prefer you can unwrap all errors and handle by your self.
#ErrorMapping(code = 401, errorType = Unauthorized.class)
#PATCH("/v1/widgets/{id}")
Single<Widget> updateWidget(#Path("id") String id, #Body Widget widget);
If you want to get data when error response comes (typically a response code except 200) you can do it like that in your onResponse() method:
override fun onResponse(call: Call<LoginData>?, response: Response<LoginData>?) {
if (response != null) {
if (response.code() == 200 && response.body() != null) {
val loginData = response.body()
if (loginData != null) {
//Handle success case...
}
} else if (response.code() == 401) {
val converter = ApiClient.getClient()?.responseBodyConverter<ErrorResponseData>(
ErrorResponseData::class.java,
arrayOfNulls<Annotation>(0))
var errorResponse: ErrorResponseData? = null
errorResponse = converter?.convert(response.errorBody())
if (errorResponse != null) {
//Handle Error case..
}
}
}
}
For Kotlin:
Just follow this code to convert your errorBody to your response:
if(response.isSuccessful){
val data = response.body()!!
}else {
val gson = GsonBuilder().create()
try {
var pojo = gson.fromJson(
response.errorBody()!!.string(),
CommentResponse::class.java)
Log.e("ERROR_CHECK","here else is the error$pojo")
} catch (e: IOException) {
// handle failure at error parse
}
}
you can do in the following way
fun parseErrorBody(readText: String?): String {
if (!readText.isNullOrEmpty()) {
val result = Gson().fromJson(readText, AppError::class.java)//AppError can be your server error response model
return result.errors?.get(0)?.message ?: Constants.EMPTY_STR
}
return ""
}
and calling code
if(response.isSuccessful){}//handle success response
else{
parseErrorBody(response.errorBody()?.charStream()?.readText())
}

Categories

Resources