2016-01-27 7 views
5

rénovation 2, il y avait une façon de gérer erros au centre -Gestion centralisée des erreurs retrofit 2? Avant

new retrofit.RestAdapter.Builder() 
     .setEndpoint(apiUrl) 
     .setLogLevel(retrofit.RestAdapter.LogLevel.FULL) 
     .setErrorHandler(new CustomErrorHandler(ctx)) 

Mais maintenant Retrofit 2, RestAdapter a été renommé Retrofit et il n'y a pas setErrorHandler(). Existe-t-il un moyen d'avoir une gestion centralisée des erreurs en utilisant Retrofit.Builder()?

Répondre

1

Après link est bien expliqué comment résoudre ce dans Retrofit 2.

Votre réponse d'erreur:

{ 
    statusCode: 409, 
    message: "Email address already registered" 
} 

Votre classe d'erreur:

public class APIError { 

    private int statusCode; 
    private String message; 

    public APIError() { 
    } 

    public int status() { 
     return statusCode; 
    } 

    public String message() { 
     return message; 
    } 
} 

et comment gérer l'erreur:

public class ErrorUtils { 

    public static APIError parseError(Response<?> response) { 
     Converter<ResponseBody, APIError> converter = 
       ServiceGenerator.retrofit() 
         .responseBodyConverter(APIError.class, new Annotation[0]); 

     APIError error; 

     try { 
      error = converter.convert(response.errorBody()); 
     } catch (IOException e) { 
      return new APIError(); 
     } 

     return error; 
    } 
} 

Votre demande doit être quelque chose comme ceci:

Call<User> call = service.me(); 
call.enqueue(new Callback<User>() { 
    @Override 
    public void onResponse(Response<User> response) { 
     if (response.isSuccess()) { 
      // use response data and do some fancy stuff :) 
     } else { 
      // parse the response body … 
      APIError error = ErrorUtils.parseError(response); 
      // … and use it to show error information 

      // … or just log the issue like we’re doing :) 
      Log.d("error message", error.message()); 
     } 
    } 

    @Override 
    public void onFailure(Throwable t) { 
     // there is more than just a failing request (like: no internet connection) 
    } 
}); 

Comme vous pouvez le voir utiliser la classe ErrorUtils pour analyser le corps d'erreur et obtenir un objet ApiError.

+0

averti que onFailing sera également déclenchée s'il y a une erreur d'analyse, mais alors vous ne être en mesure d'accéder au corps de la réponse pour essayer de récupérer l'erreur. –

+0

Bonne journée. Qu'est-ce que la méthode "responseBodyConverter"? –

+0

@AlexeiKorshun regarder [ici] (https://futurestud.io/blog/retrofit-getting-started-and-android-client) – Amir

3

Rénovation 2.0 déplacé ErrorHandler et en utilisant une nouvelle Callback qui comprend deux méthodes:

/** Successful HTTP response. */ 
public void onResponse(Response<T> response, Retrofit retrofit)```` 

/** Invoked when a network or unexpected exception occurred during the HTTP request. */ 
public void onFailure(Throwable t) 

Retrofit2.x recevra toute la réponse HTTP dans le onResponse même si le code http ne 2xx ou 3xx, ici vous devez vérifier le code d'état de réponse dans votre méthode onResponse et vérifier si la réponse de réponse de réponse (normalement 2xx ou 3xx) et faire le bon traitement logique.

J'ai mis à jour retrofit2.x et ma solution sur la gestion des erreurs est centralisée: Création d'une classe abstraite qui étend Retrofit.Callback avec deux méthodes onSuccess et onFailed, le onFailed n'est pas abstraite comme je le fais toujours le même processus lorsque le la logique métier a échoué et fait la même chose lorsque la requête est réussie. Vous pouvez consulter l'exemple de code here

puis, quand vous avez besoin d'envoyer des requêtes http, vous devez implémenter la méthode onSuccess et vous pouvez également remplacer la méthode onFailed dans certains cas, comme je l'ai mentionné dans mon projet, je processus l'échoué de la même manière dans la plupart des cas. Vous pouvez consulter l'exemple here dans lequel j'ai utilisé retrofit2 pour envoyer une demande de publication.

J'espère que cela peut vous aider!

0

J'ai utilisé une solution similaire à celle proposée par Amir, mais je me demande simplement si cela pourrait être encore plus simple. J'ai essayé ce qui suit:

public void onResponse(Response<T> response) { 
     if (response.isSuccess()) { 
      T resp = response.body(); 
      handleSuccessResponse(resp); 

     } else { 
      Response<StatusResponse> statusResponse = response.error(response.code(), response.errorBody()); 
      handleHttpErrorResponse(statusResponse); 
     } 
    } 

De cette façon je n'aurais pas besoin de passer l'instance Retrofit autour. Cependant, il me manque quelque chose car le corps de l'erreur n'est pas analysé avec succès à StatusResponse. Je ne suis pas sûr de ce que cela signifie en pratique:

La modification de 2.0.0-beta2 qui a fourni l'instance Retrofit au rappel onResponse de Callback a été annulée.Il y a trop de cas de contour pour fournir l'objet Retrofit afin de permettre la désérialisation du corps de l'erreur. Pour adapter ce cas d'utilisation, passez la réponse Retrofit manuellement ou implémentez un CallAdapter.Factory personnalisé le fait automatiquement.

2.0.0-beta3

3

Vous devez faire un CallAdapter personnalisé avec vos callbacks de succès ou d'erreur de service personnalisé. Jake Wharton en a déjà fait un. Vous pouvez trouver sur retrofit/échantillons

PS: pas applicable aux versions antérieures à 2.2.0

/* 
* Copyright (C) 2015 Square, Inc. 
* 
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
*  http://www.apache.org/licenses/LICENSE-2.0 
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed under the License is distributed on an "AS IS" BASIS, 
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License. 
*/ 
package com.example.retrofit; 

import java.io.IOException; 
import java.lang.annotation.Annotation; 
import java.lang.reflect.ParameterizedType; 
import java.lang.reflect.Type; 
import java.util.concurrent.Executor; 
import retrofit2.Call; 
import retrofit2.CallAdapter; 
import retrofit2.Callback; 
import retrofit2.converter.gson.GsonConverterFactory; 
import retrofit2.Response; 
import retrofit2.Retrofit; 
import retrofit2.http.GET; 

/** 
* A sample showing a custom {@link CallAdapter} which adapts the built-in {@link Call} to a custom 
* version whose callback has more granular methods. 
*/ 
public final class ErrorHandlingAdapter { 
    /** A callback which offers granular callbacks for various conditions. */ 
    interface MyCallback<T> { 
    /** Called for [200, 300) responses. */ 
    void success(Response<T> response); 
    /** Called for 401 responses. */ 
    void unauthenticated(Response<?> response); 
    /** Called for [400, 500) responses, except 401. */ 
    void clientError(Response<?> response); 
    /** Called for [500, 600) response. */ 
    void serverError(Response<?> response); 
    /** Called for network errors while making the call. */ 
    void networkError(IOException e); 
    /** Called for unexpected errors while making the call. */ 
    void unexpectedError(Throwable t); 
    } 

    interface MyCall<T> { 
    void cancel(); 
    void enqueue(MyCallback<T> callback); 
    MyCall<T> clone(); 

    // Left as an exercise for the reader... 
    // TODO MyResponse<T> execute() throws MyHttpException; 
    } 

    public static class ErrorHandlingCallAdapterFactory extends CallAdapter.Factory { 
    @Override public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, 
     Retrofit retrofit) { 
     if (getRawType(returnType) != MyCall.class) { 
     return null; 
     } 
     if (!(returnType instanceof ParameterizedType)) { 
     throw new IllegalStateException(
      "MyCall must have generic type (e.g., MyCall<ResponseBody>)"); 
     } 
     Type responseType = getParameterUpperBound(0, (ParameterizedType) returnType); 
     Executor callbackExecutor = retrofit.callbackExecutor(); 
     return new ErrorHandlingCallAdapter<>(responseType, callbackExecutor); 
    } 

    private static final class ErrorHandlingCallAdapter<R> implements CallAdapter<R, MyCall<R>> { 
     private final Type responseType; 
     private final Executor callbackExecutor; 

     ErrorHandlingCallAdapter(Type responseType, Executor callbackExecutor) { 
     this.responseType = responseType; 
     this.callbackExecutor = callbackExecutor; 
     } 

     @Override public Type responseType() { 
     return responseType; 
     } 

     @Override public MyCall<R> adapt(Call<R> call) { 
     return new MyCallAdapter<>(call, callbackExecutor); 
     } 
    } 
    } 

    /** Adapts a {@link Call} to {@link MyCall}. */ 
    static class MyCallAdapter<T> implements MyCall<T> { 
    private final Call<T> call; 
    private final Executor callbackExecutor; 

    MyCallAdapter(Call<T> call, Executor callbackExecutor) { 
     this.call = call; 
     this.callbackExecutor = callbackExecutor; 
    } 

    @Override public void cancel() { 
     call.cancel(); 
    } 

    @Override public void enqueue(final MyCallback<T> callback) { 
     call.enqueue(new Callback<T>() { 
     @Override public void onResponse(Call<T> call, Response<T> response) { 
      // TODO if 'callbackExecutor' is not null, the 'callback' methods should be executed 
      // on that executor by submitting a Runnable. This is left as an exercise for the reader. 

      int code = response.code(); 
      if (code >= 200 && code < 300) { 
      callback.success(response); 
      } else if (code == 401) { 
      callback.unauthenticated(response); 
      } else if (code >= 400 && code < 500) { 
      callback.clientError(response); 
      } else if (code >= 500 && code < 600) { 
      callback.serverError(response); 
      } else { 
      callback.unexpectedError(new RuntimeException("Unexpected response " + response)); 
      } 
     } 

     @Override public void onFailure(Call<T> call, Throwable t) { 
      // TODO if 'callbackExecutor' is not null, the 'callback' methods should be executed 
      // on that executor by submitting a Runnable. This is left as an exercise for the reader. 

      if (t instanceof IOException) { 
      callback.networkError((IOException) t); 
      } else { 
      callback.unexpectedError(t); 
      } 
     } 
     }); 
    } 

    @Override public MyCall<T> clone() { 
     return new MyCallAdapter<>(call.clone(), callbackExecutor); 
    } 
    } 
}