Centralized error handling 2?

Before finalizing 2, there was a way to deal with err centrally -

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

But now in Retrofit 2, the RestAdapter has been renamed to Retrofit , and there is no setErrorHandler() . Is there a way to centrally handle error handling using Retrofit.Builder() ?

+5
source share
4 answers

Retrofit 2.0 moved the ErrorHandler and used the new Callback , which includes two methods:

 /** 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 will receive the whole HTTP response in onResponse , although the http code is not 2xx or 3xx, here you need to check the response status code in your onResponse method and check whether the response matches the response (usually 2xx or 3xx) and perform the correct logical processing .

I updated retrofit2.x and my decision on centralized error handling: Creating an abstract class that extends Retrofit.Callback using the two methods onSuccess and onFailed, onFailed is not abstract, as I always do the same process when the business logic failed , and performs another task when the request is successful. You can refer to the sample code here

then when you need to send an HTTP request, you need to implement the onSuccess method, and in some cases you can also override the onFailed method, as I mentioned in my project, in most cases I handle a failed failure. You can reference the example here , which I used retrofit2 to send a message.

Hope this helps you!

+3
source

You must create your own CallAdapter with your special service successes or callbacks. Jake Wharton has already done one. You can find it on retrofit / samples

PS: not applicable to versions prior to 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 (eg, 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); } } } 
+3
source

After it is well explained how to solve this problem in Retrofit 2.

Your error response:

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

Error class:

 public class APIError { private int statusCode; private String message; public APIError() { } public int status() { return statusCode; } public String message() { return message; } } 

And how to deal with the error:

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

Your query should look something like this:

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

As you can see, use the ErrorUtils class to parse the error body and get an APIError object.

+1
source

I used a similar solution, as Amir suggests, but I'm just wondering if this can be done even easier. I tried the following:

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

This way, I will not need to go through an instance of Retrofit. However, I am missing something because the body of the error is not successfully processed by StatusResponse. I'm not sure what this means in practice:

The cancellation in 2.0.0-beta2, which provided the Retrofit instance for the onResponse callback callback, was canceled. Too many edge cases around the Retrofit object to allow deserialization of the error body. To place this use case, either manually respond to Retrofit or make your own CallAdapter.Factory. This is automatic.

2.0.0-beta3

0
source

Source: https://habr.com/ru/post/1241575/


All Articles