Retrofit 2.0 nasıl serileştirilmiş hata yanıtı alınır. Gövde


130

Ben kullanıyorum Retrofitler 2.0.0-beta1 .

Testlerde alternatif bir senaryom var ve HTTP 400 hatası bekliyorum

Sahip olmak isterdim retrofit.Response<MyError> response amaresponse.body() == null

MyError serileştirilmemiş değil - sadece burada görüyorum

response.errorBody().string()

ama bana nesne olarak MyError'ı vermiyor



Hata yanıtını seri durumdan çıkarmak iyi bir uygulama mı? çünkü yanıt html olan bir web sunucusu hatası olabilir.
Hossein Shahdoost

1
thx @ahmadalibaloch, bu bağlantı gerçekten çok yardımcı.
Ravi Vaniya

Yanıtlar:


139

Şu anda dönüştürücüler veya özel sınıflar kullanmayı gerektirmeyen çok kolay bir uygulama kullanıyorum. Kullandığım kod şu:

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();
        }
    }
}

6
Çözümünüz bir hata yanıtı içeriği göstermiyor.
CoolMind

1
Düzenlememi kontrol edin, neden ilk başta onu bu kadar belirsiz yaptığımı bilmiyorum.
Saif Bechan

4
Son olarak, işe yarayan bir android sorusuna basit bir yanıt (çoğu android yanıtı komik bir şekilde karmaşıktır).
Doug Voss

Bu kesinlikle soruyu cevaplamıyor. Hata Enum Nesnesini değil, yalnızca hata mesajını döndürür. Lütfen şu yanıtı izleyin: stackoverflow.com/a/21103420/2914140
Tobliug

Cevapta "mesaj" için bir eşleme arıyor, ancak hata cevabım buna sahip değildi. "Hata" ile eşlemesi vardı. Yani okuyan herkes, aldığınız cevaba bağlıdır!
mco

44

ErrorResponse , özel yanıt nesnenizdir

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);

3
opsiyonelleri açmaya zorlamamalısınız:gson.fromJson(response.errorBody()?.charStream(), type)
hopeman

37

Bunu şu şekilde çözdüm:

 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 Sınıfı:

  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;
     }
   }

2
java.lang.IllegalStateException: BEGIN_OBJECT bekleniyordu, ancak 1. satırda STRING, sütun 2 yol $
Ronel Gonzales

Kullanın .addConverterFactory(ScalarsConverterFactory.create())@RonelGonzales
Pratik Butani

30

Retrofit 2.0 beta2'de şu şekilde hata yanıtları alıyorum:

  1. Senkron

    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. eşzamanlı olmayan

    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
        }
    });

Retrofit 2 beta3 güncellemesi:

  1. Eşzamanlı - değiştirilmedi
  2. Eşzamansız - Güçlendirme parametresi onResponse'dan kaldırıldı

    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
        }
    });

4
BasicResponse'de ne var?
Jemshit Iskenderov

2
Sadece mesaj ve hata kodunu içeren basit bir Jackson açıklamalı sınıfı. Her durumda, orada sunucu yanıt türünüzle eşleşen herhangi bir açıklamalı sınıfa sahip olabilirsiniz. İhtiyaçlarınıza uygun bir tane oluşturmak için jsonschema2pojo kullanmayı deneyin .
JFreeman

Diğerleri için, bunun yerine şunu kullanabilirsiniz: Converter <ResponseBody, <Message> errorConverter = retrofit.responseBodyConverter (Message.class, new Annotation [0]);
Kim Montano

@JFreeman, ya seriyi kaldırmak istersem List<BasicResponse>?
azizbekian

benim için benim
başvurum

10

Gelen https://stackoverflow.com/a/21103420/2914140 ve https://futurestud.io/tutorials/retrofit-2-simple-error-handling bu varyant Recommended 2.1.0 gösterilmiştir.

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();
            }
        }
    }

10

Yanıtı ona dönüştürmek için bir Hata yanıtı ve kullanıcı Gson modeli oluşturun. Bu sadece iyi çalışacak.

APIError.java

public class APIError {
    private String message;

    public String getMessage() {
        return message;
    }
}

MainActivity.java (İç istek 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();
}

9
 @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();
                }

            }

7

Retrofit 2.0-beta2 kullanarak asenkron aramalar için bunu şu şekilde yaptım:

@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();
        }
    }
}

MyError sınıfı ne olacak?
Dhrupal

OnResponse'un bir Çağrı parametresi ve bir Yanıt parametresi içermesi gerektiğini düşündüm. Sizinkinin Retrofit parametresi nasıl oluyor?
Marty Miller

@MartyMiller bu, Retrofit 2.0-beta2'nin aşağıdaki versiyonu için yapıldı
shantanu

7

Kotlin kullanıyorsanız, başka bir çözüm yalnızca Response sınıfı için uzantı işlevi oluşturmak olabilir:

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
}

kullanım

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

Sonunda birisi kodun okunmasını kolaylaştırmak için Kotlin gücünü kullandı!
Vince

6

Aslında çok basit.

Kotlin:

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

Java:

JSONObject jsonObj = new JSONObject(response.errorBody().charStream().readText());
responseInterface.onFailure(jsonObj.getString("msg"));

Güçlendirme üzerinde test edildi: 2.5.0. Size bir String verecek olan charStream'deki metni okuyun, ardından JSONObject'e ayrıştırın.

Adios.


readText()TextStreamsKt.readText(response.errorBody().charStream())
java'da

4

Ben de aynı sorunla karşı karşıyaydım. Güçlendirme ile çözdüm. Bunu göstereyim ...

Hata JSON yapınız gibiyse

{
"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;
   }
}

Ve bu benim Hata durumu sınıfım

public class ErrorStatus {

  @SerializedName("status")
  @Expose
  private String status;

  public String getStatus() {
      return status;
  }

  public void setStatus(String status) {
      this.status = status;
  }
}

Şimdi json'umuzu idare edebilecek bir sınıfa ihtiyacımız var.

  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;
}
}

Artık retrofit api çağrısındaki cevabımızı kontrol edebiliriz

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) {

        }
    });
}

İşte şimdi Tostunuzu gösterebilirsiniz


4

KotlinUzantıları kullanarak şık bir çözüm :

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
}

ve kullanım:

showError(retrofitThrowable.getApiError()?.message)


3

Bu şekilde, yalnızca Retrofit ile oluşturulan bir hizmeti enjekte ediyorsanız, Retrofit örneğine ihtiyacınız olmaz.

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;
  }
}

Bunu şu şekilde kullanın:

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();
    }
  }

3

ErrorBody'yi bir String'e okuyun ve json'u manuel olarak ayrıştırın.

 if(!response.isSuccessful()) {
   
    String error = "";
    try {
        BufferedReader ereader = new BufferedReader(new InputStreamReader(
                response.errorBody().byteStream()));
        String eline = null;
        while ((eline = ereader.readLine()) != null) {
            error += eline + "";
        }
        ereader.close();
    } catch (Exception e) {
        error += e.getMessage();
    }
    Log.e("Error",error);
    
    try {
        JSONObject reader = new JSONObject(error);
        String message = reader.getString("message");
    
        Toast.makeText(context,message,Toast.LENGTH_SHORT).show();
    
    } catch (JSONException e) {
        e.printStackTrace();
    }
 }

2

OkHttp'yi Retrofit ile birlikte kullandığınızda sorun bu gibi görünüyor, bu nedenle ya OkHttp'yi kaldırabilir ya da hata gövdesini almak için aşağıdaki kodu kullanabilirsiniz:

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(); 
}
}

0

çözdü:

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

GsonConverterFactory ile nasıl dönüştürebilirim? Herhangi bir fikir ?
Shan Xeeshi

Bir yol buldum. Sadece değiştirmek JacksonConverterFactory için GsonConverterFactorybenim özel nesnesine json dönüştürür ancak uyarı verir denetlenmeyen döküm retrofit.Converter <yakalama <>>?
Shan Xeeshi

3
MyError sınıfında ne var?
Dhrupal

0
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();
            }

0

Kotlin'de:

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) {
    }
})

0

errorBody değerleri, Retrofit içinde APIError nesnesini ayarlamalıdır. Bunun için aşağıdaki kod yapısını kullanabilirsiniz.

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) {
    ....
}

0

Test edildi ve çalışıyor

 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;
        }

-1
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)`
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.