web-dev-qa-db-fra.com

Modernisation et gestion des erreurs centralisée

Chaque demande au serveur peut renvoyer error_code. Je veux gérer ces erreurs à un endroit Lorsque j'utilisais AsyncTask, j'avais une baseAsyncTask comme celle-ci. 

public abstract class BaseAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {

    protected Context context;
    private ProgressDialog progressDialog;
    private Result result;

    protected BaseAsyncTask(Context context, ProgressDialog progressDialog) {
        this.context = context;
        this.progressDialog = progressDialog;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override
    protected void onPostExecute(Result result) {
        super.onPostExecute(result);
        HttpResponse<ErrorResponse> response = (HttpResponse<ErrorResponse>) result;
     if(response.getData().getErrorCode() != -1) {
                handleErrors(response.getData());
        }else 
            onResult(result);

    }

    private void handleErrors(ErrorResponse errorResponse) {
    }
     public abstract void onResult(Result result);
    }

Cependant, l’utilisation de la modification de chaque demande a son rappel de traitement des erreurs:

                    git.getFeed(user,new Callback<gitmodel>() {
                    @Override
                    public void success(gitmodel gitmodel, Response response) {

                    }

                    @Override
                    public void failure(RetrofitError error) {

                    }
                });
            }
        });

Comment puis-je gérer toutes les erreurs au même endroit?

15
Ma7moud El-Naggar

Si vous devez obtenir une erreur 'logic', vous avez besoin d'une logique Java, car ce n'est pas une fonctionnalité de modernisation, donc:

  1. Créer un rappel de votre implémentation qui implémente le rappel de modification
  2. Créer un objet de base qui définit la méthode 'isError'
  3. Modifier Retrofit RestAdapter afin d’obtenir votre rappel au lieu du Retrofit One

MyCallback.Java

import Android.util.Log;
import retrofit.Callback;
import retrofit.client.Response;

public abstract class MyCallback<T extends MyObject> implements Callback<T> {

    @Override
    public final void success(T o, Response response) {
        if (o.isError()) {
            // [..do something with error]
            handleLogicError(o);
        }
        else {
            handleSuccess(o, response);
        }
    }

    abstract void handleSuccess(T o, Response response);

    void handleLogicError(T o) {
        Log.v("TAG", "Error because userId is " + o.id);
    }
}

MyObject.Java (la classe de base pour tous vos objets obtenus avec Retrofit)

public class MyObject {
    public long id;
    public boolean isError() {
        return id == 1;
    }
}

MyRealObject.Java - une classe qui étend l'objet de base

public class MyRealObject extends MyObject {
    public long userId;
    public String title;
    public String body;
}

RetroInterface.Java - l’interface utilisée par Retrofit avec laquelle vous devriez être familiarisé

import retrofit.http.GET;
import retrofit.http.Path;

public interface RetroInterface {

    @GET("/posts/{id}")
    void sendGet(@Path("id") int id, MyCallback<MyRealObject> callback);

}

Et enfin le morceau de code où vous utilisez toute la logique

    RestAdapter adapter = new RestAdapter.Builder()
            .setEndpoint("http://jsonplaceholder.typicode.com")
            .build();

    RetroInterface itf = adapter.create(RetroInterface.class);
    itf.sendGet(2, new MyCallback<MyRealObject>() {
        @Override
        void handleSuccess(MyRealObject o, Response response) {
            Log.v("TAG", "success");
        }

        @Override
        public void failure(RetrofitError error) {
            Log.v("TAG", "failure");
        }
    });

Si vous copiez et collez ce code, vous obtiendrez une erreur lorsque vous exécuterez la fonction itf.sendGet(1, new MyCallback..) et la réussite de la fonction itf.sendGet(2, new MyCallback...)

18
Mimmo Grottoli

Je ne suis pas sûr d'avoir bien compris, mais vous pouvez créer un rappel et le transmettre en tant que paramètre à toutes vos demandes. 

Au lieu de:

            git.getFeed(user,new Callback<gitmodel>() {
                @Override 
                public void success(gitmodel gitmodel, Response response) { 

                } 

                @Override 
                public void failure(RetrofitError error) { 

                } 
            }); 

Commencez par définir votre rappel:

Callback<gitmodel> mCallback = new Callback<gitmodel>() {
    @Override 
    public void success(gitmodel gitmodel, Response response) { 

    } 

    @Override 
    public void failure(RetrofitError error) { 
        // logic to handle error for all requests
    } 
};

Ensuite:

git.getFeed(user, mCallback);
7
Teo Inke

Dans Retrofit, vous pouvez spécifier ErrorHandler pour toutes les demandes.

public class ApiErrorHandler implements ErrorHandler {

    @Override
    public Throwable handleError(RetrofitError cause) {
        //here place your logic for all errors
        return cause;
    }
}

Appliquez-le à RestAdapter

RestAdapter.Builder()
            .setClient(client)
            .setEndpoint(endpoint)
            .setErrorHandler(errorHandler)
            .build();

Je pense que c'est ce que vous avez demandé.

6
jakubbialkowski

Dans Retrofit2, vous ne pouvez pas définir un ErrorHandler avec la méthode .setErrorHandler(), mais vous pouvez créer un intercepteur pour extraire toutes les erreurs possibles centralisées à un emplacement de votre application. 

Avec cet exemple, vous avez un emplacement centralisé pour la gestion de vos erreurs avec Retrofit2 et OkHttpClient. Il suffit de réutiliser l’objet Retrofit (retrofit).

Vous pouvez essayer cet exemple autonome avec un intercepteur personnalisé pour les erreurs de réseau et de serveur. Ces deux éléments seront traités différemment dans Retrofit2, vous devez donc vérifier le code d'erreur renvoyé par le serveur par rapport au code de réponse (response.code()) et si la réponse a échoué (!response.isSuccessful()).

Si l'utilisateur n'a pas de connexion au réseau ou au serveur, vous devez intercepter une exception IOException de la méthode Response response = chain.proceed(chain.request()); et gérer l'erreur réseau dans le bloc catch.

HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
    loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

    OkHttpClient client = new OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .addInterceptor(new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    try {
                        Response response = chain.proceed(chain.request());
                        if (!response.isSuccessful()) {
                            Log.e("tag", "Failure central - response code: " + response.code());
                            Log.e("tag", "central server error handling");

                            // Central error handling for error responses here:
                            // e.g. 4XX and 5XX errors
                            switch (response.code()) {
                                case 401:
                                    // do something when 401 Unauthorized happened
                                    // e.g. delete credentials and forward to login screen
                                    // ...

                                    break;
                                case 403:
                                    // do something when 403 Forbidden happened
                                    // e.g. delete credentials and forward to login screen
                                    // ...

                                    break;
                                default:
                                    Log.e("tag", "Log error or do something else with error code:" + response.code());

                                    break;
                            }
                        }

                        return response;
                    } catch (IOException e) {
                        // Central error handling for network errors here:
                        // e.g. no connection to internet / to server

                        Log.e("tag", e.getMessage(), e);
                        Log.e("tag", "central network error handling");

                        throw e;
                    }
                }
            })
            .build();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://10.0.2.2:8000/api/v1/")
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    UserRepository backendRepository = retrofit.create(UserRepository.class);
    backendRepository.getUser("userId123").enqueue(new Callback<UserModel>() {
        @Override
        public void onResponse(Call<UserModel> call, retrofit2.Response<UserModel> response) {
            Log.d("tag", "onResponse");

            if (!response.isSuccessful()) {
                Log.e("tag", "onFailure local server error handling code:" + response.code());
            } else {
                // its all fine with the request


            }
        }

        @Override
        public void onFailure(Call<UserModel> call, Throwable t) {
            Log.e("tag", "onFailure local network error handling");
            Log.e("tag", t.getMessage(), t);

        }
    });

Exemple de UserRepository:

public interface UserRepository {
    @GET("users/{userId}/")
    Call<UserModel> getUser(@Path("userId") String userId);

}

Exemple UserModel:

public class UserModel implements Parcelable {
    @SerializedName("id")
    @Expose
    public String id = "";

    @SerializedName("email")
    @Expose
    public String mail = "";

    public UserModel() {

    }

    protected UserModel(Parcel in) {
        id = in.readString();
        mail = in.readString();
    }

    public static final Creator<UserModel> CREATOR = new Creator<UserModel>() {
        @Override
        public UserModel createFromParcel(Parcel in) {
            return new UserModel(in);
        }

        @Override
        public UserModel[] newArray(int size) {
            return new UserModel[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(id);
        dest.writeString(mail);
    }
}
1
Andrej1A

Assez simplement Retrofit exemple de traitement d'erreur personnalisée. Est configuré de manière à ne pas nécessiter beaucoup de travail dans le gestionnaire "échec" d'un appel de modification pour obtenir le message d'erreur visible par l'utilisateur à afficher. Fonctionne sur tous les points finaux. Il y a beaucoup de gestion des exceptions car nos serveurs aiment nous garder sur nos gardes en envoyant toutes sortes de choses aléatoires ..! 

     retrofit-custom-error-handling.Java
    // on error the server sends JSON
    /*
    { "error": { "data": { "message":"A thing went wrong" } } }
    */
    // create model classes..
    public class ErrorResponse {
    Error error;
    public static class Error {
    Data data;
    public static class Data {
    String message;
    }
    }
    }
    //
    /**
    * Converts the complex error structure into a single string you can get with error.getLocalizedMessage() in Retrofit error handlers.
    * Also deals with there being no network available
    *
    * Uses a few string IDs for user-visible error messages
    */
    private static class CustomErrorHandler implements ErrorHandler {
    private final Context ctx;

    public CustomErrorHandler(Context ctx) {
        this.ctx = ctx;
    }

    @Override
    public Throwable handleError(RetrofitError cause) {
        String errorDescription;
        if (cause.isNetworkError()) {
            errorDescription = ctx.getString(R.string.error_network);
        } else {
            if (cause.getResponse() == null) {
                errorDescription = ctx.getString(R.string.error_no_response);
            } else {
// Error message handling - return a simple error to Retrofit handlers..
                try {
                    ErrorResponse errorResponse = (ErrorResponse) cause.getBodyAs(ErrorResponse.class);
                    errorDescription = errorResponse.error.data.message;
                } catch (Exception ex) {
                    try {
                        errorDescription = ctx.getString(R.string.error_network_http_error, cause.getResponse().getStatus());
                    } catch (Exception ex2) {
                        Log.e(TAG, "handleError: " + ex2.getLocalizedMessage());
                        errorDescription = ctx.getString(R.string.error_unknown);
                    }
                }
            }
        }
        return new Exception(errorDescription);
    }
}
// When creating the Server...
retrofit.RestAdapter restAdapter = new retrofit.RestAdapter.Builder()
        .setEndpoint(apiUrl)
        .setLogLevel(retrofit.RestAdapter.LogLevel.FULL)
        .setErrorHandler(new CustomErrorHandler(ctx)) // use error handler..
        .build();
server=restAdapter.create(Server.class);
// Now when calling server methods, get simple error out like this:
        server.postSignIn(login,new Callback<HomePageResponse>(){
@Override
public void success(HomePageResponse homePageResponse,Response response){
// Do success things!
        }
@Override
public void failure(RetrofitError error){
        error.getLocalizedMessage(); // <-- this is the message to show to user.
        }
        });
0
kuljeet singh