18

在改造 2 之前,有一种集中处理错误的方法 -

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

但现在在 Retrofit 2 中,RestAdapter 已重命名为,Retrofit并且没有setErrorHandler(). 有没有办法使用集中式错误处理Retrofit.Builder()

4

4 回答 4

16

好吧,您所要做的就是CallAdapter使用您的自定义回调进行自定义,以应对失败的情况。Retrofit repo 有一个示例显示自定义CallAdapter. 您可以在retrofit/samples中找到它。

这是一个显示自定义的示例CallAdapter(不适用于 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 javax.annotation.Nullable;
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 @Nullable 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);
    }
  }
}

以下是您可以如何使用它:
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("http://httpbin.org")
    .addCallAdapterFactory(new ErrorHandlingCallAdapterFactory())
    .addConverterFactory(GsonConverterFactory.create())
    .build();

HttpBinService service = retrofit.create(HttpBinService.class);
MyCall<Ip> ip = service.getIp();

ip.enqueue(new MyCallback<Ip>() {

    @Override public void success(Response<Ip> response) {
    System.out.println("SUCCESS! " + response.body().origin);
    }

    @Override public void unauthenticated(Response<?> response) {
    System.out.println("UNAUTHENTICATED");
    }

    @Override public void clientError(Response<?> response) {
    System.out.println("CLIENT ERROR " + response.code() + " " + response.message());
    }

    @Override public void serverError(Response<?> response) {
    System.out.println("SERVER ERROR " + response.code() + " " + response.message());
    }

    @Override public void networkError(IOException e) {
    System.err.println("NETWORK ERROR " + e.getMessage());
    }

    @Override public void unexpectedError(Throwable t) {
    System.err.println("FATAL ERROR " + t.getMessage());
    }
});
于 2017-05-14T08:16:02.953 回答
6

Retrofit 2.0 移动了 ErrorHandler 并使用了一个Callback包含两种方法的新方法:

/** 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 会收到所有的 HTTP 响应,onResponse即使 http 码不是 2xx 或 3xx,这里你需要检查你的onResponse方法中的响应状态码并检查响应是否成功响应(通常是 2xx 或 3xx)并做对逻辑处理。

我已经升级了retrofit2.x,我关于集中式错误处理的解决方案是:创建一个抽象类,用两个方法onSuccess和onFailed扩展Retrofit.Callback,onFailed不是抽象的,因为我总是在业务逻辑失败时执行相同的过程并执行请求成功时的不同之处。您可以在此处参考示例代码

然后,当您需要发送 http 请求时,您需要实现 onSuccess 方法,并且在某些情况下您还可以覆盖 onFailed 方法,正如我在我的项目中提到的,我在大多数情况下以相同的方式处理失败。您可以参考我使用 retrofit2 发送发布请求的示例here 。

希望这可以帮到你!

于 2016-01-27T08:32:06.180 回答
0

我使用了与 Amir 提出的类似解决方案,但我只是想知道这是否可以变得更容易。我尝试了以下方法:

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

这样我就不需要传递 Retrofit 实例了。但是我遗漏了一些东西,因为错误正文没有成功解析为 StatusResponse。我不确定这在实践中意味着什么:

2.0.0-beta2 中将 Retrofit 实例提供给 Callback 的 onResponse 回调的更改已恢复。为了允许对错误主体进行反序列化,提供 Retrofit 对象有太多的边缘情况。为了适应这种用例,请手动传递 Retrofit 响应或实现自定义 CallAdapter.Factory 会自动执行此操作。

2.0.0-beta3

于 2016-02-12T07:34:13.137 回答
0

对于集中处理特定于 401 的案例以及使用新的身份验证令牌重试请求,请参阅此堆栈溢出答案

于 2018-09-16T05:40:56.493 回答