web-dev-qa-db-de.com

Retrofit 2.0, wie Sie eine deserialisierte Fehlerantwort erhalten

Ich benutze Retrofit 2.0.0-beta1 .

In Tests habe ich ein alternatives Szenario und erwarte den Fehler HTTP 400

Ich hätte gerne retrofit.Response<MyError> response Aber response.body() == null

MyError wird nicht deserialisiert - ich sehe es nur hier

response.errorBody().string()

aber es gibt mir nicht MyError als Objekt

80
Piotr Boho

Ich verwende derzeit eine sehr einfache Implementierung, für die keine Konverter oder spezielle Klassen verwendet werden müssen. Der Code, den ich verwende, ist folgender:

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.getString("message"), Toast.LENGTH_LONG).show();
        } catch (Exception e) {
            Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_LONG).show();
        }
    }
}
87
Saif Bechan

In Retrofit 2.0 Beta2 erhalte ich auf diese Weise Fehlerantworten:

  1. Synchron 

    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. Asynchron

    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 für Retrofit 2 beta3:

  1. Synchron - nicht geändert
  2. Asynchron - Der Retrofit-Parameter wurde aus onResponse entfernt

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

Ich habe es gelöst durch:

 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;
     }
   }
21
Pooja Gupta

In https://stackoverflow.com/a/21103420/2914140 und https://futurestud.io/tutorials/retrofit-2-simple-error-handling Diese Variante wird für Retrofit 2.1 angezeigt. 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();
            }
        }
    }
8
CoolMind

Ich habe es so für asynchrone Aufrufe mit Retrofit 2.0-beta2 gemacht:

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

            }
6
Vins

ErrorResponse ist Ihr benutzerdefiniertes Antwortobjekt

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

Auf diese Weise benötigen Sie keine Retrofit-Instanz, wenn Sie nur einen aus Retrofit erstellten Dienst einschleusen. 

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

Verwenden Sie es so:

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
Codeversed

Dies scheint das Problem zu sein, wenn Sie OkHttp zusammen mit Retrofit verwenden. Sie können also entweder OkHttp entfernen oder den folgenden Code verwenden, um den Fehlerrumpf abzurufen:

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

Ich war mit demselben Problem konfrontiert. Ich habe es mit Retrofit gelöst. Lass mich das zeigen ... 

Wenn deine Fehler JSON-Struktur sind 

{
"error": {
    "status": "The email field is required."
}
}


My ErrorRespnce.Java 

public class ErrorResponce {

   @SerializedName("error")
   @Expose
   private ErrorStatus error;

   public ErrorStatus getError() {
      return error;
   }

   public void setError(ErrorStatus error) {
      this.error = error;
   }
}

Und dies ist meine Fehlerstatusklasse

public class ErrorStatus {

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

  public String getStatus() {
      return status;
  }

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

Jetzt brauchen wir eine Klasse, die mit unserem Json umgehen kann. 

  public class ErrorUtils {

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

Jetzt können wir unsere Antwort im nachgerüsteten api-Aufruf überprüfen 

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


                ErrorResponce errorResponce = ErrorUtils.parseError(response);
                Toast.makeText(MainActivity.this, ""+errorResponce.getError().getStatus(), Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        public void onFailure(Call<RegistrationResponce> call, Throwable t) {

        }
    });
}

Jetzt können Sie Ihren Toast zeigen

2
pavel

Wenn Sie Kotlin verwenden, kann eine andere Lösung darin bestehen, eine Erweiterungsfunktion für die Antwortklasse zu erstellen:

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
}

Verwendungszweck

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

Wie wäre es mit einer verallgemeinerten Lösung? Ich denke, so etwas kann funktionieren:

abstract class TestCallback<RESPONSE, ERROR extends Throwable> implements Callback<RESPONSE> {

    Class<ERROR> errorClass;
    Retrofit retrofit;

    TestCallback(Retrofit retrofit, Class<ERROR> errorClass) {
        this.retrofit = retrofit;
        this.errorClass = errorClass;
    }

    abstract void onSuccess(Call<RESPONSE> call, RESPONSE response);

    @Override
    public void onResponse(Call<RESPONSE> call, Response<RESPONSE> response) {
        if (response.isSuccessful()) {
            onSuccess(call, response.body());
            return;
        }

        if (response.errorBody() != null) {
            Converter<ResponseBody, ERROR> converter = retrofit.responseBodyConverter(errorClass, new Annotation[0]);
            ERROR error;
            try {
                error = converter.convert(response.errorBody());
                onFailure(call, error);
            } catch (IOException e) {
                // Conversion error. Add some meaningful message or return a custom error.                
                onFailure(call, new Throwable());
            }
        } else {
            // Unknown HTTP error (errorBody == null). Add some meaningful message or return a custom error.                
            onFailure(call, new Throwable());
        }
    }
}

Und dann können wir diese benutzerdefinierte Callback<> auf folgende Weise verwenden:

Call<User> call = retrofit.create(UsersApi.class).signUp(email, password);
call.enqueue(new TestCallback<User, TestError>(retrofit, TestError.class) {
        @Override
        public void onFailure(Call<User> call, Throwable t) {
            if (t instanceof TestError) {

            } else {

            }
        }

        @Override
        void onSuccess(Call<User> call, User response) {
            // No need to check for isSuccessful() + no need to 
            // duplicate the same code for all of your handlers.
        }
    });
1
fraggjkee

Lesen Sie errorBody in einen String und analysieren Sie json manuell.

 if(!response.isSuccessful()) {
    response.errorBody();

    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();
    }
 }
1
Riyas PK
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
Mike6679

errorBody-Werte sollten das APIError-Objekt in Retrofit festlegen. So können Sie die folgende Codestruktur verwenden.

öffentliche Klasse 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 (Antwort); If (error.getStatus () == 400) { ....}

0
Egemen Mede

gelöst durch:

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

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) {
    }
})
0
Adam Johns