Retrofit 2.0 hoe een gedeserialiseerde fout response.body te krijgen

Ik gebruik Retrofit 2.0.0-beta1.

Tijdens tests heb ik een alternatief scenario en verwacht ik fout HTTP 400

Ik wil graag retrofit.Response<MyError> response
maar response.body() == null

MyError is niet gedeserialiseerd – ik zie het alleen hier

response.errorBody().string()

maar ik krijg MyError niet als object


Antwoord 1, autoriteit 100%

Ik gebruik momenteel een zeer eenvoudige implementatie, waarvoor geen converters of speciale klassen nodig zijn. De code die ik gebruik is de volgende:

public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
    DialogHelper.dismiss();
    if (response.isSuccessful()) {
        // Do your success stuff...
    } else {
        try {
            JSONObject jObjError = new JSONObject(response.errorBody().string());
            Toast.makeText(getContext(), jObjError.getJSONObject("error").getString("message"), Toast.LENGTH_LONG).show();
        } catch (Exception e) {
            Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_LONG).show();
        }
    }
}

Een punt om op te merken is dat response.errorBody().string()slechts één keer de juiste waarde zal retourneren. Als u het opnieuw aanroept, wordt een lege tekenreeks geretourneerd. Dus als je het opnieuw wilt gebruiken, sla de waarde dan op in een variabele bij de eerste aanroep.


Antwoord 2, autoriteit 42%

ErrorResponseis uw aangepaste responsobject

Kotlin

val gson = Gson()
val type = object : TypeToken<ErrorResponse>() {}.type
var errorResponse: ErrorResponse? = gson.fromJson(response.errorBody()!!.charStream(), type)

Java

Gson gson = new Gson();
Type type = new TypeToken<ErrorResponse>() {}.getType();
ErrorResponse errorResponse = gson.fromJson(response.errorBody.charStream(),type);

Antwoord 3, autoriteit 24%

Ik heb het opgelost door:

if(!response.isSuccessful()){
       Gson gson = new Gson();
       MyErrorMessage message=gson.fromJson(response.errorBody().charStream(),MyErrorMessage.class);
       if(message.getCode()==ErrorCode.DUPLICATE_EMAIL_ID_CODE){
                  //DO Error Code specific handling                        
        }else{
                 //DO GENERAL Error Code Specific handling                               
        }
    }

MyErrorMessage-klasse:

 public class MyErrorMessage {
     private int code;
     private String message;
     public int getCode() {
        return code;
     }
     public void setCode(int code) {
        this.code = code;
     }
     public String getMessage() {
         return message;
     }
     public void setMessage(String message) {
        this.message = message;
     }
   }

Antwoord 4, autoriteit 17%

In Retrofit 2.0 beta2 krijg ik op deze manier foutmeldingen:

  1. Synchroon

    try {
       Call<RegistrationResponse> call = backendServiceApi.register(data.in.account, data.in.password,
               data.in.email);
       Response<RegistrationResponse> response = call.execute();
       if (response != null && !response.isSuccess() && response.errorBody() != null) {
           Converter<ResponseBody, BasicResponse> errorConverter =
                   MyApplication.getRestClient().getRetrofitInstance().responseConverter(BasicResponse.class, new Annotation[0]);
           BasicResponse error = errorConverter.convert(response.errorBody());
           //DO ERROR HANDLING HERE
           return;
       }
       RegistrationResponse registrationResponse = response.body();
       //DO SUCCESS HANDLING HERE
    } catch (IOException e) {
       //DO NETWORK ERROR HANDLING HERE
    }
    
  2. Asynchroon

    Call<BasicResponse> call = service.loadRepo();
    call.enqueue(new Callback<BasicResponse>() {
        @Override
        public void onResponse(Response<BasicResponse> response, Retrofit retrofit) {
            if (response != null && !response.isSuccess() && response.errorBody() != null) {
                Converter<ResponseBody, BasicResponse> errorConverter =
                    retrofit.responseConverter(BasicResponse.class, new Annotation[0]);
                BasicResponse error = errorConverter.convert(response.errorBody());
                //DO ERROR HANDLING HERE
                return;
            }
            RegistrationResponse registrationResponse = response.body();
            //DO SUCCESS HANDLING HERE
        }
        @Override
        public void onFailure(Throwable t) {
            //DO NETWORK ERROR HANDLING HERE
        }
    });
    

Update voor Retrofit 2 beta3:

  1. Synchroon – niet gewijzigd
  2. Asynchroon – Retrofit-parameter is verwijderd uit onResponse

    Call<BasicResponse> call = service.loadRepo();
    call.enqueue(new Callback<BasicResponse>() {
        @Override
        public void onResponse(Response<BasicResponse> response) {
            if (response != null && !response.isSuccess() && response.errorBody() != null) {
                Converter<ResponseBody, BasicResponse> errorConverter =
                    MyApplication.getRestClient().getRetrofitInstance().responseConverter(BasicResponse.class, new Annotation[0]);
                BasicResponse error = errorConverter.convert(response.errorBody());
                //DO ERROR HANDLING HERE
                return;
            }
            RegistrationResponse registrationResponse = response.body();
            //DO SUCCESS HANDLING HERE
        }
        @Override
        public void onFailure(Throwable t) {
            //DO NETWORK ERROR HANDLING HERE
        }
    });
    

Antwoord 5, autoriteit 17%

Het is eigenlijk heel eenvoudig.

Kotlin:

val jsonObj = JSONObject(response.errorBody()!!.charStream().readText())
responseInterface.onFailure(jsonObj.getString("msg"))

Java:

   if(response.errorBody()!=null){
    JSONObject jsonObj = new JSONObject(TextStreamsKt.readText(response.errorBody().charStream()));
        responseInterface.onFailure(jsonObj.getString("msg"));
    }else{
        responseInterface.onFailure("you might want to return a generic error message.");
    }

Getest bij retrofit: 2.5.0.
Lees de tekst van de charStream die je een String geeft en ontleden vervolgens naar JSONObject.

Adios.


Antwoord 6, autoriteit 14%

Maak een model van de foutreactie & gebruiker Gson om het antwoord ernaar te converteren. Dit werkt prima.

APIERror.java

public class APIError {
    private String message;
    public String getMessage() {
        return message;
    }
}

MainActivity.java(binnen verzoek onResponse)

if (response.isSuccessful()) {
    // Do your success stuff...
} else {
    APIError message = new Gson().fromJson(response.errorBody().charStream(), APIError.class);
    Toast.makeText(MainActivity.this, "" + message.getMessage(), Toast.LENGTH_SHORT).show();
}

Antwoord 7, autoriteit 6%

@Override
 public void onResponse(Call<Void> call, retrofit2.Response<Void> response) {
            if (response.isSuccessful()) {
            //Do something if response is ok
            } else {
                JsonParser parser = new JsonParser();
                JsonElement mJson = null;
                try {
                    mJson = parser.parse(response.errorBody().string());
                    Gson gson = new Gson();
                    MyError errorResponse = gson.fromJson(mJson, MyError.class);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }

Antwoord 8, autoriteit 6%

In https://stackoverflow.com/a/21103420/2914140en https://futurestud.io/tutorials/retrofit-2-simple-error-handlingdeze variant wordt getoond voor Retrofit 2.1.0.

call.enqueue(new Callback<MyResponse>() {
    @Override
    public void onResponse(Call<MyResponse> call, Response<MyResponse> response) {
        if (response.isSuccessful()) {
            ...
        } else {
            Converter<ResponseBody, MyError> converter
                    = MyApplication.getRetrofit().responseBodyConverter(
                    MyError.class, new Annotation[0]);
            MyError errorResponse = null;
            try {
                errorResponse = converter.convert(response.errorBody());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

Antwoord 9, autoriteit 4%

Als je Kotlin gebruikt, kan een andere oplossing zijn om gewoon een extensiefunctie voor de Response-klasse te maken:

inline fun <reified T>Response<*>.parseErrJsonResponse(): T?
{
    val moshi = MyCustomMoshiBuilder().build()
    val parser = moshi.adapter(T::class.java)
    val response = errorBody()?.string()
    if(response != null)
        try {
            return parser.fromJson(response)
        } catch(e: JsonDataException) {
            e.printStackTrace()
        }
    return null
}

Gebruik

val myError = response.parseErrJsonResponse<MyErrorResponse>()
if(myError != null) {
   // handle your error logic here
   // ...
}

Antwoord 10, autoriteit 3%

Ik deed het op deze manier voor asynchrone oproepen met Retrofit 2.0-beta2:

@Override
public void onResponse(Response<RegistrationResponse> response, 
                       Retrofit retrofit) {
    if (response.isSuccess()) {
        // Do success handling here
    } else {
        try {
            MyError myError = (MyError)retrofit.responseConverter(
                    MyError.class, MyError.class.getAnnotations())
                .convert(response.errorBody());
            // Do error handling here
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Antwoord 11, autoriteit 3%

Ik had hetzelfde probleem. Ik heb het opgelost met retrofit. Laat me dit laten zien…

Als uw fout JSON-structuur is zoals

{
"error": {
    "status": "The email field is required."
}
}
My ErrorRespnce.java 
public class ErrorResponse {
   @SerializedName("error")
   @Expose
   private ErrorStatus error;
   public ErrorStatus getError() {
      return error;
   }
   public void setError(ErrorStatus error) {
      this.error = error;
   }
}

En dit is mijn foutstatusklasse

public class ErrorStatus {
  @SerializedName("status")
  @Expose
  private String status;
  public String getStatus() {
      return status;
  }
  public void setStatus(String status) {
      this.status = status;
  }
}

Nu hebben we een klasse nodig die onze json aankan.

 public class ErrorUtils {
   public static ErrorResponse parseError (Response<?> response){
      Converter<ResponseBody , ErrorResponse> converter =          ApiClient.getClient().responseBodyConverter(ErrorResponse.class , new Annotation[0]);
    ErrorResponse errorResponse;
    try{
        errorResponse = converter.convert(response.errorBody());
    }catch (IOException e){
        return new ErrorResponse();
    }
    return errorResponse;
}
}

Nu kunnen we onze reactie controleren in de retrofit API-aanroep

private void registrationRequest(String name , String email , String password , String c_password){
    final Call<RegistrationResponce> registrationResponceCall = apiInterface.getRegistration(name , email , password , c_password);
    registrationResponceCall.enqueue(new Callback<RegistrationResponce>() {
        @Override
        public void onResponse(Call<RegistrationResponce> call, Response<RegistrationResponce> response) {
            if (response.code() == 200){
            }else if (response.code() == 401){
                ErrorResponse errorResponse = ErrorUtils.parseError(response);
                Toast.makeText(MainActivity.this, ""+errorResponse.getError().getStatus(), Toast.LENGTH_SHORT).show();
            }
        }
        @Override
        public void onFailure(Call<RegistrationResponce> call, Throwable t) {
        }
    });
}

Dat is het, nu kun je je Toast laten zien


Antwoord 12, autoriteit 3%

Hier is een elegante oplossing met behulp van Kotlin-extensies:

data class ApiError(val code: Int, val message: String?) {
    companion object {
        val EMPTY_API_ERROR = ApiError(-1, null)
    }
}
fun Throwable.getApiError(): ApiError? {
    if (this is HttpException) {
        try {
            val errorJsonString = this.response()?.errorBody()?.string()
            return Gson().fromJson(errorJsonString, ApiError::class.java)
        } catch (exception: Exception) {
            // Ignore
        }
    }
    return EMPTY_API_ERROR
}

en gebruik:

showError(retrofitThrowable.getApiError()?.message)


Antwoord 13, autoriteit 3%

if(!response.isSuccessful()) {
    StringBuilder error = new StringBuilder();
    try {
        BufferedReader bufferedReader = null;
        if (response.errorBody() != null) {
            bufferedReader = new BufferedReader(new InputStreamReader(
                    response.errorBody().byteStream()));
            String eLine = null;
            while ((eLine = bufferedReader.readLine()) != null) {
                error.append(eLine);
            }
            bufferedReader.close();
        }
    } catch (Exception e) {
        error.append(e.getMessage());
    }
    Log.e("Error", error.toString());
}

Antwoord 14, autoriteit 2%

Op deze manier heeft u geen Retrofit-instantie nodig als u alleen een service injecteert die is gemaakt met Retrofit.

public class ErrorUtils {
  public static APIError parseError(Context context, Response<?> response) {
    APIError error = new APIError();
    try {
        Gson gson = new Gson();
        error = gson.fromJson(response.errorBody().charStream(), APIError.class);
    } catch (Exception e) {
        Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
    }
    if (TextUtils.isEmpty(error.getErrorMessage())) {
        error.setError(response.raw().message());
    }
    return error;
  }
}

Gebruik het als volgt:

if (response.isSuccessful()) {
      ...
    } else {
      String msg = ErrorUtils.parseError(fragment.getActivity(), response).getError(); // would be from your error class
      Snackbar.make(someview, msg, Snackbar.LENGTH_LONG).show();
    }
  }

Antwoord 15

Dit lijkt het probleem te zijn wanneer u OkHttp samen met Retrofit gebruikt, dus u kunt OkHttp verwijderen of onderstaande code gebruiken om de hoofdtekst van de fout te krijgen:

if (!response.isSuccessful()) {
 InputStream i = response.errorBody().byteStream();
 BufferedReader r = new BufferedReader(new InputStreamReader(i));
 StringBuilder errorResult = new StringBuilder();
 String line;
 try {
   while ((line = r.readLine()) != null) {
   errorResult.append(line).append('\n');
   }
 } catch (IOException e) { 
    e.printStackTrace(); 
}
}

Antwoord 16

Er zijn al veel geldige antwoorden. Dit is slechts een toevoeging voor een gebruikssituatie, wanneer u dezelfde Retrofit-reactie meer dan eens moet gebruiken. Geen van beide kan worden gebruikt, zoals u kunt de hoofdtekst van de reactie maar één keer lezen, omdat deze daarna wordt gesloten en u elke volgende keer nullkrijgt, wanneer u probeert te lezen van hetzelfde antwoordobject:

response()?.errorBody()?.charStream()?.readText()
response()?.errorBody()?.string()

In plaats daarvan kunt u een alleen-lezen kopie van de antwoordreeks krijgen (terwijl de reactie zelf kan worden doorgegeven en uiteindelijk later kan worden gebruikt):

response()?.errorBody()?.source()?.buffer?.snapshot()?.utf8()

Antwoord 17

opgelost door:

Converter<MyError> converter = 
    (Converter<MyError>)JacksonConverterFactory.create().get(MyError.class);
MyError myError =  converter.fromBody(response.errorBody());

Antwoord 18

try{
                ResponseBody response = ((HttpException) t).response().errorBody();
                JSONObject json = new JSONObject( new String(response.bytes()) );
                errMsg = json.getString("message");
            }catch(JSONException e){
                return t.getMessage();
            }
            catch(IOException e){
                return t.getMessage();
            }

Antwoord 19

In Kotlin:

val call = APIClient.getInstance().signIn(AuthRequestWrapper(AuthRequest("1234567890z", "12341234", "nonce")))
call.enqueue(object : Callback<AuthResponse> {
    override fun onResponse(call: Call<AuthResponse>, response: Response<AuthResponse>) {
        if (response.isSuccessful) {
        } else {
            val a = object : Annotation{}
            val errorConverter = RentalGeekClient.getRetrofitInstance().responseBodyConverter<AuthFailureResponse>(AuthFailureResponse::class.java, arrayOf(a))
            val authFailureResponse = errorConverter.convert(response.errorBody())
        }
    }
    override fun onFailure(call: Call<AuthResponse>, t: Throwable) {
    }
})

Antwoord 20

errorBody-waarden moeten het APIError-object in Retrofit instellen. Zodat u de onderstaande codestructuur kunt gebruiken.

public class APIErrorUtils {
    public static APIError parseError(Response<?> response) {
        Converter<ResponseBody, APIError> converter = API.getClient().responseBodyConverter(APIError.class, new Annotation[0]);
        APIError error;
        try {
            error = converter.convert(response.errorBody());
            Log.d("SERVICELOG", "****************************************************");
            Log.d("SERVICELOG", "***** SERVICE LOG");
            Log.d("SERVICELOG", "***** TIMESTAMP: " + String.valueOf(error.getTimestamp()));
            Log.d("SERVICELOG", "***** STATUS: " + String.valueOf(error.getStatus()));
            Log.d("SERVICELOG", "***** ERROR: " + error.getError());
            Log.d("SERVICELOG", "***** MESSAGE: " + error.getMessage());
            Log.d("SERVICELOG", "***** PATH: " + error.getPath());
            Log.d("SERVICELOG", "****************************************************");
        } catch (IOException e) {
            return new APIError();
        }
        return error;
    }
}
APIError error = APIErrorUtils.parseError(response);
if (error.getStatus() == 400) {
    ....
}

Antwoord 21

Getest en werkt

public BaseModel parse(Response<BaseModel> response , Retrofit retrofit){
            BaseModel error = null;
            Converter<ResponseBody, BaseModel> errorConverter =
                    retrofit.responseBodyConverter(BaseModel.class, new Annotation[0]);
            try {
                if (response.errorBody() != null) {
                    error = errorConverter.convert(response.errorBody());
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return error;
        }

Antwoord 22

val error = JSONObject(callApi.errorBody()?.string() as String)
            CustomResult.OnError(CustomNotFoundError(userMessage = error["userMessage"] as String))
open class CustomError (
    val traceId: String? = null,
    val errorCode: String? = null,
    val systemMessage: String? = null,
    val userMessage: String? = null,
    val cause: Throwable? = null
)
open class ErrorThrowable(
    private val traceId: String? = null,
    private val errorCode: String? = null,
    private val systemMessage: String? = null,
    private val userMessage: String? = null,
    override val cause: Throwable? = null
) : Throwable(userMessage, cause) {
    fun toError(): CustomError = CustomError(traceId, errorCode, systemMessage, userMessage, cause)
}
class NetworkError(traceId: String? = null, errorCode: String? = null, systemMessage: String? = null, userMessage: String? = null, cause: Throwable? = null):
    CustomError(traceId, errorCode, systemMessage, userMessage?: "Usted no tiene conexión a internet, active los datos", cause)
class HttpError(traceId: String? = null, errorCode: String? = null, systemMessage: String? = null, userMessage: String? = null, cause: Throwable? = null):
    CustomError(traceId, errorCode, systemMessage, userMessage, cause)
class UnknownError(traceId: String? = null, errorCode: String? = null, systemMessage: String? = null, userMessage: String? = null, cause: Throwable? = null):
    CustomError(traceId, errorCode, systemMessage, userMessage?: "Unknown error", cause)
class CustomNotFoundError(traceId: String? = null, errorCode: String? = null, systemMessage: String? = null, userMessage: String? = null, cause: Throwable? = null):
    CustomError(traceId, errorCode, systemMessage, userMessage?: "Data not found", cause)`

Antwoord 23

Fout bij verwerking van carrosserie in kotlin Android

catch (cause: Throwable) {
            when (cause) {
                is HttpException -> {
                    try {
                        val YourErrorResponseClassObj = Gson().fromJson(cause.response()?.errorBody()?.charStream(), YourErrorResponseClass::class.java)
                    } catch (e: Exception) {
                    }
                }
                else -> {
                    //Other errors like Network ...
                }
            }
        }

Antwoord 24

heel eenvoudig. en dit redt mijn leven ooit

public static void displayApiResponseErrorBody(Response<?> response)
{
    InputStream i = response.errorBody().byteStream();
    BufferedReader r = new BufferedReader(new InputStreamReader(i));
    StringBuilder errorResult = new StringBuilder();
    String line;
    try {
        while ((line = r.readLine()) != null) 
        {
            errorResult.append(line).append('\n');
        }
        Log.d("API_RESPONSE_ERROR_BODY",String.valueOf(errorResult));
        System.out.println(errorResult);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Other episodes